From d5a2d8e1ceddd91ee3d9f6f3c621456cf183cfc7 Mon Sep 17 00:00:00 2001 From: Eirik Tsarpalis Date: Wed, 21 Oct 2026 16:47:36 +0300 Subject: [PATCH 1/9] Use extension-member polyfills in System.Text.Json Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 3c692d8a-03a2-4ed9-b337-2066c25a3cb6 --- .../Common/src/Polyfills/EnumPolyfills.cs | 6 ++ .../Polyfills/MemoryExtensionsPolyfills.cs | 26 ++++++++ .../Polyfills/ReadOnlySequencePolyfills.cs | 13 ++++ .../Common/src/Polyfills/StringPolyfills.cs | 28 ++++++++ .../Common/src/Polyfills/TypePolyfills.cs | 34 ++++++++++ .../Common/src/Polyfills/UInt64Polyfills.cs | 23 +++++++ .../Common/src/System/Buffers/SpanAction.cs | 6 ++ .../Common/src/System/Net/ArrayBuffer.cs | 10 +-- .../Common/src/System/StringPolyfills.cs | 2 +- ...icrosoft.Extensions.Logging.Console.csproj | 1 + .../src/System.Net.ServerSentEvents.csproj | 1 + .../Common/JsonCamelCaseNamingPolicy.cs | 8 +-- .../gen/Helpers/RoslynExtensions.cs | 65 ++++++++++--------- .../gen/JsonSourceGenerator.Parser.cs | 2 +- .../System.Text.Json.SourceGeneration.targets | 5 ++ .../src/System.Text.Json.csproj | 8 +++ .../Text/Json/Document/JsonDocument.Parse.cs | 4 +- .../src/System/Text/Json/JsonHelpers.cs | 29 --------- .../src/System/Text/Json/Nodes/JsonArray.cs | 7 -- .../Text/Json/Reader/JsonReaderHelper.cs | 9 --- .../Reader/Utf8JsonReader.MultiSegment.cs | 19 +----- .../System/Text/Json/Reader/Utf8JsonReader.cs | 2 +- .../Converters/Value/EnumConverter.cs | 22 +------ .../JsonSerializer.Read.Utf8JsonReader.cs | 6 +- .../Serialization/Metadata/JsonTypeInfo.cs | 26 +------- ...flectionEmitCachingMemberAccessor.Cache.cs | 4 -- .../Json/Serialization/PipeReadBufferState.cs | 6 +- .../JsonNode/JsonArrayTests.cs | 26 ++++++++ .../Serialization/EnumConverterTests.cs | 30 +++++++++ .../Serialization/JsonElementTests.cs | 26 ++++++++ .../Serialization/NamingPolicyUnitTests.cs | 15 +++++ .../Utf8JsonReaderTests.MultiSegment.cs | 33 ++++++++++ 32 files changed, 333 insertions(+), 169 deletions(-) create mode 100644 src/libraries/Common/src/Polyfills/ReadOnlySequencePolyfills.cs create mode 100644 src/libraries/Common/src/Polyfills/StringPolyfills.cs create mode 100644 src/libraries/Common/src/Polyfills/TypePolyfills.cs create mode 100644 src/libraries/Common/src/Polyfills/UInt64Polyfills.cs create mode 100644 src/libraries/Common/src/System/Buffers/SpanAction.cs diff --git a/src/libraries/Common/src/Polyfills/EnumPolyfills.cs b/src/libraries/Common/src/Polyfills/EnumPolyfills.cs index 806a3a722865ba..c86958a15f2605 100644 --- a/src/libraries/Common/src/Polyfills/EnumPolyfills.cs +++ b/src/libraries/Common/src/Polyfills/EnumPolyfills.cs @@ -8,6 +8,12 @@ internal static class EnumPolyfills { extension(Enum) { + public static string[] GetNames() where TEnum : struct, Enum => + Enum.GetNames(typeof(TEnum)); + + public static TEnum[] GetValues() where TEnum : struct, Enum => + (TEnum[])Enum.GetValues(typeof(TEnum)); + public static TEnum Parse(string value, bool ignoreCase) where TEnum : struct, Enum => (TEnum)Enum.Parse(typeof(TEnum), value, ignoreCase); } diff --git a/src/libraries/Common/src/Polyfills/MemoryExtensionsPolyfills.cs b/src/libraries/Common/src/Polyfills/MemoryExtensionsPolyfills.cs index 6c5f6bcefa0082..55897954f1208a 100644 --- a/src/libraries/Common/src/Polyfills/MemoryExtensionsPolyfills.cs +++ b/src/libraries/Common/src/Polyfills/MemoryExtensionsPolyfills.cs @@ -27,6 +27,32 @@ public static bool ContainsAnyExcept(this ReadOnlySpan span, T value) wher public static bool ContainsAny(this ReadOnlySpan span, ReadOnlySpan values) => span.IndexOfAny(values) >= 0; + public static int IndexOfAnyExcept(this ReadOnlySpan span, byte value) + { + for (int i = 0; i < span.Length; i++) + { + if (span[i] != value) + { + return i; + } + } + + return -1; + } + + public static int LastIndexOfAnyExcept(this ReadOnlySpan span, byte value) + { + for (int i = span.Length - 1; i >= 0; i--) + { + if (span[i] != value) + { + return i; + } + } + + return -1; + } + public static int Count(this ReadOnlySpan span, T value) where T : IEquatable { int count = 0; diff --git a/src/libraries/Common/src/Polyfills/ReadOnlySequencePolyfills.cs b/src/libraries/Common/src/Polyfills/ReadOnlySequencePolyfills.cs new file mode 100644 index 00000000000000..54f33d69f1c869 --- /dev/null +++ b/src/libraries/Common/src/Polyfills/ReadOnlySequencePolyfills.cs @@ -0,0 +1,13 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Buffers; + +/// Provides downlevel polyfills for instance members on . +internal static class ReadOnlySequencePolyfills +{ + extension(ReadOnlySequence sequence) + { + public ReadOnlySpan FirstSpan => sequence.First.Span; + } +} diff --git a/src/libraries/Common/src/Polyfills/StringPolyfills.cs b/src/libraries/Common/src/Polyfills/StringPolyfills.cs new file mode 100644 index 00000000000000..0bd60fa411c2bb --- /dev/null +++ b/src/libraries/Common/src/Polyfills/StringPolyfills.cs @@ -0,0 +1,28 @@ +// 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; + +namespace System; + +/// Provides a downlevel polyfill for creating strings with a span callback. +internal static partial class StringPolyfills +{ + extension(string) + { + public static string Create(int length, TState state, SpanAction action) + { + ArgumentNullException.ThrowIfNull(action); + ArgumentOutOfRangeException.ThrowIfNegative(length); + + if (length == 0) + { + return string.Empty; + } + + char[] chars = new char[length]; + action(chars, state); + return new string(chars); + } + } +} diff --git a/src/libraries/Common/src/Polyfills/TypePolyfills.cs b/src/libraries/Common/src/Polyfills/TypePolyfills.cs new file mode 100644 index 00000000000000..94eb7045c66405 --- /dev/null +++ b/src/libraries/Common/src/Polyfills/TypePolyfills.cs @@ -0,0 +1,34 @@ +// 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 instance members on . +internal static class TypePolyfills +{ + extension(Type type) + { + public bool IsByRefLike + { + get + { + if (!type.IsValueType) + { + return false; + } + + object[] attributes = type.GetCustomAttributes(inherit: false); + + for (int i = 0; i < attributes.Length; i++) + { + if (attributes[i].GetType().FullName == "System.Runtime.CompilerServices.IsByRefLikeAttribute") + { + return true; + } + } + + return false; + } + } + } +} diff --git a/src/libraries/Common/src/Polyfills/UInt64Polyfills.cs b/src/libraries/Common/src/Polyfills/UInt64Polyfills.cs new file mode 100644 index 00000000000000..c1f021ae69baf3 --- /dev/null +++ b/src/libraries/Common/src/Polyfills/UInt64Polyfills.cs @@ -0,0 +1,23 @@ +// 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 UInt64Polyfills +{ + extension(ulong) + { + public static ulong PopCount(ulong value) + { + ulong count = 0; + while (value != 0) + { + value &= value - 1; + count++; + } + + return count; + } + } +} diff --git a/src/libraries/Common/src/System/Buffers/SpanAction.cs b/src/libraries/Common/src/System/Buffers/SpanAction.cs new file mode 100644 index 00000000000000..aab4dea590b74b --- /dev/null +++ b/src/libraries/Common/src/System/Buffers/SpanAction.cs @@ -0,0 +1,6 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Buffers; + +internal delegate void SpanAction(Span span, TArg arg); diff --git a/src/libraries/Common/src/System/Net/ArrayBuffer.cs b/src/libraries/Common/src/System/Net/ArrayBuffer.cs index c99c8a943eea06..5d4e2c54025f4d 100644 --- a/src/libraries/Common/src/System/Net/ArrayBuffer.cs +++ b/src/libraries/Common/src/System/Net/ArrayBuffer.cs @@ -21,12 +21,6 @@ namespace System.Net [StructLayout(LayoutKind.Auto)] internal struct ArrayBuffer : IDisposable { -#if NET - private static int ArrayMaxLength => Array.MaxLength; -#else - private const int ArrayMaxLength = 0X7FFFFFC7; -#endif - private readonly bool _usePool; private byte[] _bytes; private int _activeStart; @@ -156,13 +150,13 @@ private void EnsureAvailableSpaceCore(int byteCount) int desiredSize = ActiveLength + byteCount; - if ((uint)desiredSize > ArrayMaxLength) + if ((uint)desiredSize > Array.MaxLength) { throw new OutOfMemoryException(); } // Double the existing buffer size (capped at Array.MaxLength). - int newSize = Math.Max(desiredSize, (int)Math.Min(ArrayMaxLength, 2 * (uint)_bytes.Length)); + int newSize = Math.Max(desiredSize, (int)Math.Min(Array.MaxLength, 2 * (uint)_bytes.Length)); byte[] newBytes = _usePool ? ArrayPool.Shared.Rent(newSize) : diff --git a/src/libraries/Common/src/System/StringPolyfills.cs b/src/libraries/Common/src/System/StringPolyfills.cs index da679b268204f4..3c06c119fefcb8 100644 --- a/src/libraries/Common/src/System/StringPolyfills.cs +++ b/src/libraries/Common/src/System/StringPolyfills.cs @@ -6,7 +6,7 @@ namespace System { /// Provides downlevel polyfills for string extension methods. - internal static class StringPolyfills + internal static partial class StringPolyfills { public static bool StartsWith(this string s, char value) => s.Length > 0 && s[0] == value; diff --git a/src/libraries/Microsoft.Extensions.Logging.Console/src/Microsoft.Extensions.Logging.Console.csproj b/src/libraries/Microsoft.Extensions.Logging.Console/src/Microsoft.Extensions.Logging.Console.csproj index c61223da404128..8bfa0c8da677f9 100644 --- a/src/libraries/Microsoft.Extensions.Logging.Console/src/Microsoft.Extensions.Logging.Console.csproj +++ b/src/libraries/Microsoft.Extensions.Logging.Console/src/Microsoft.Extensions.Logging.Console.csproj @@ -31,6 +31,7 @@ + diff --git a/src/libraries/System.Net.ServerSentEvents/src/System.Net.ServerSentEvents.csproj b/src/libraries/System.Net.ServerSentEvents/src/System.Net.ServerSentEvents.csproj index 5679e432168200..5c898f2a6048c9 100644 --- a/src/libraries/System.Net.ServerSentEvents/src/System.Net.ServerSentEvents.csproj +++ b/src/libraries/System.Net.ServerSentEvents/src/System.Net.ServerSentEvents.csproj @@ -26,6 +26,7 @@ System.Net.ServerSentEvents.SseParser + diff --git a/src/libraries/System.Text.Json/Common/JsonCamelCaseNamingPolicy.cs b/src/libraries/System.Text.Json/Common/JsonCamelCaseNamingPolicy.cs index 43fa642a3c6d2d..c2a4df85eed758 100644 --- a/src/libraries/System.Text.Json/Common/JsonCamelCaseNamingPolicy.cs +++ b/src/libraries/System.Text.Json/Common/JsonCamelCaseNamingPolicy.cs @@ -12,17 +12,11 @@ public override string ConvertName(string name) return name; } -#if NET return string.Create(name.Length, name, (chars, name) => { - name.CopyTo(chars); + name.AsSpan().CopyTo(chars); FixCasing(chars); }); -#else - char[] chars = name.ToCharArray(); - FixCasing(chars); - return new string(chars); -#endif } private static void FixCasing(Span chars) diff --git a/src/libraries/System.Text.Json/gen/Helpers/RoslynExtensions.cs b/src/libraries/System.Text.Json/gen/Helpers/RoslynExtensions.cs index 76d2f53b20f3d4..2d4e8873f49677 100644 --- a/src/libraries/System.Text.Json/gen/Helpers/RoslynExtensions.cs +++ b/src/libraries/System.Text.Json/gen/Helpers/RoslynExtensions.cs @@ -802,45 +802,50 @@ private static bool HasCodeAnalysisAttribute(this ISymbol symbol, string attribu // Source-symbol candidate enumeration mirrored by these polyfills: // https://github.com/dotnet/roslyn/blob/18bf2c8709264bac6615856e507eb44ba2a026e2/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs#L903-L961 - /// - /// Polyfill for ITypeSymbol.IsClosed: returns when - /// is an abstract class declared with the closed modifier, whose - /// hierarchy the language restricts to its declaring module. Detection is syntactic because the - /// compiler-emitted [IsClosedType] attribute is filtered out of the symbol API. When - /// available, the built-in API is used through light-up so metadata-only symbols are supported; - /// otherwise source declarations fall back to syntax inspection. - /// - public static bool IsClosedType(this INamedTypeSymbol type) + extension(ITypeSymbol type) { - if (type is not { TypeKind: TypeKind.Class, IsAbstract: true }) + /// + /// Gets a value indicating whether the type is an abstract class declared with the + /// closed modifier, whose hierarchy the language restricts to its declaring module. + /// Detection is syntactic because the compiler-emitted [IsClosedType] attribute is + /// filtered out of the symbol API. When available, the built-in API is used through light-up + /// so metadata-only symbols are supported; otherwise source declarations fall back to syntax inspection. + /// + public bool IsClosed { - return false; - } + get + { + if (type is not { TypeKind: TypeKind.Class, IsAbstract: true }) + { + return false; + } - if (s_isClosedTypeAccessor is not null) - { - return s_isClosedTypeAccessor(type); - } + if (s_isClosedTypeAccessor is not null) + { + return s_isClosedTypeAccessor(type); + } - foreach (SyntaxReference syntaxReference in type.DeclaringSyntaxReferences) - { - if (syntaxReference.GetSyntax() is BaseTypeDeclarationSyntax declaration) - { - foreach (SyntaxToken modifier in declaration.Modifiers) + foreach (SyntaxReference syntaxReference in type.DeclaringSyntaxReferences) { - // The 'closed' contextual keyword is tokenized as a dedicated SyntaxKind by - // closed-aware compilers, but that enum member does not exist in the Roslyn - // version the generator compiles against. Compare the token text instead, which - // is stable across compiler versions. - if (modifier.Text is "closed") + if (syntaxReference.GetSyntax() is BaseTypeDeclarationSyntax declaration) { - return true; + foreach (SyntaxToken modifier in declaration.Modifiers) + { + // The 'closed' contextual keyword is tokenized as a dedicated SyntaxKind by + // closed-aware compilers, but that enum member does not exist in the Roslyn + // version the generator compiles against. Compare the token text instead, which + // is stable across compiler versions. + if (modifier.Text is "closed") + { + return true; + } + } } } + + return false; } } - - return false; } private static Func? CreateIsClosedTypeAccessor() @@ -891,7 +896,7 @@ static void GetClosedDerivedTypesCore( ? candidate.ConstructUnboundGenericType() : candidate; - if (candidate.IsClosedType()) + if (candidate.IsClosed) { GetClosedDerivedTypesCore(derivedType, enumeratedTypes, ref derivedTypes); } diff --git a/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Parser.cs b/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Parser.cs index 6adeb1a0370f5a..e39a36a8282de0 100644 --- a/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Parser.cs +++ b/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Parser.cs @@ -1127,7 +1127,7 @@ namedUnionType is not null && INamedTypeSymbol? closedBaseType = null; if ((shouldInferClosedTypePolymorphism || needsRuntimeInferenceGuard || inferClosedTypePolymorphismOverride is true) && typeToGenerate.Type is INamedTypeSymbol namedBaseType && - namedBaseType.IsClosedType()) + namedBaseType.IsClosed) { closedBaseType = namedBaseType; } diff --git a/src/libraries/System.Text.Json/gen/System.Text.Json.SourceGeneration.targets b/src/libraries/System.Text.Json/gen/System.Text.Json.SourceGeneration.targets index a98f5285287eb7..eab16791f1f7c4 100644 --- a/src/libraries/System.Text.Json/gen/System.Text.Json.SourceGeneration.targets +++ b/src/libraries/System.Text.Json/gen/System.Text.Json.SourceGeneration.targets @@ -22,6 +22,11 @@ + + + + + diff --git a/src/libraries/System.Text.Json/src/System.Text.Json.csproj b/src/libraries/System.Text.Json/src/System.Text.Json.csproj index 1c09a4a0f6897f..8737cb00fc98b1 100644 --- a/src/libraries/System.Text.Json/src/System.Text.Json.csproj +++ b/src/libraries/System.Text.Json/src/System.Text.Json.csproj @@ -390,9 +390,17 @@ The System.Text.Json library is built-in as part of the shared framework in .NET + + + + + + + + diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Document/JsonDocument.Parse.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Document/JsonDocument.Parse.cs index 71e356c2936bec..3e141ba7d09b39 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Document/JsonDocument.Parse.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Document/JsonDocument.Parse.cs @@ -605,8 +605,8 @@ internal static bool TryParseValue( valueSequence = sequence.Slice(reader.TokenStartIndex, payloadLength); Debug.Assert( - valueSequence.First.Span[0] == (byte)'"', - $"Calculated sequence starts with {valueSequence.First.Span[0]}"); + valueSequence.FirstSpan[0] == (byte)'"', + $"Calculated sequence starts with {valueSequence.FirstSpan[0]}"); Debug.Assert( valueSequence.ToArray()[payloadLength - 1] == (byte)'"', diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/JsonHelpers.cs b/src/libraries/System.Text.Json/src/System/Text/Json/JsonHelpers.cs index f5f9f4ffb8b764..4fd89bb6411ee3 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/JsonHelpers.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/JsonHelpers.cs @@ -520,43 +520,14 @@ static void ParseNumber( static int IndexOfLastLeadingZero(ReadOnlySpan span) { -#if NET int firstNonZero = span.IndexOfAnyExcept((byte)'0'); return firstNonZero < 0 ? span.Length - 1 : firstNonZero - 1; -#else - for (int i = 0; i < span.Length; i++) - { - if (span[i] != '0') - { - return i - 1; - } - } - - return span.Length - 1; -#endif } static int IndexOfFirstTrailingZero(ReadOnlySpan span) { -#if NET int lastNonZero = span.LastIndexOfAnyExcept((byte)'0'); return lastNonZero == span.Length - 1 ? -1 : lastNonZero + 1; -#else - if (span.IsEmpty) - { - return -1; - } - - for (int i = span.Length - 1; i >= 0; i--) - { - if (span[i] != '0') - { - return i == span.Length - 1 ? -1 : i + 1; - } - } - - return 0; -#endif } } } diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Nodes/JsonArray.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Nodes/JsonArray.cs index 83115660323805..bff4d7d9587d75 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Nodes/JsonArray.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Nodes/JsonArray.cs @@ -162,14 +162,7 @@ private void InitializeFromSpan(ReadOnlySpan items) { List list = new(items.Length); -#if NET list.AddRange(items); -#else - foreach (JsonNode? item in items) - { - list.Add(item); - } -#endif for (int i = 0; i < list.Count; i++) { diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Reader/JsonReaderHelper.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Reader/JsonReaderHelper.cs index cf0f1a68a9362f..200b3db0386757 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Reader/JsonReaderHelper.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Reader/JsonReaderHelper.cs @@ -102,16 +102,7 @@ public static (int, int) CountNewLines(ReadOnlySpan data) { newLines = 1; data = data.Slice(0, lastLineFeedIndex); -#if NET newLines += data.Count(JsonConstants.LineFeed); -#else - int pos; - while ((pos = data.IndexOf(JsonConstants.LineFeed)) >= 0) - { - newLines++; - data = data.Slice(pos + 1); - } -#endif } return (newLines, lastLineFeedIndex); diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Reader/Utf8JsonReader.MultiSegment.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Reader/Utf8JsonReader.MultiSegment.cs index 41ba50b804d8c0..db040025777965 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Reader/Utf8JsonReader.MultiSegment.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Reader/Utf8JsonReader.MultiSegment.cs @@ -23,7 +23,7 @@ public ref partial struct Utf8JsonReader /// public Utf8JsonReader(ReadOnlySequence jsonData, bool isFinalBlock, JsonReaderState state) { - _buffer = jsonData.First.Span; + _buffer = jsonData.FirstSpan; _isFinalBlock = isFinalBlock; _isInputSequence = true; @@ -634,20 +634,7 @@ private static int FindMismatch(ReadOnlySpan span, ReadOnlySpan lite { Debug.Assert(span.Length > 0); - int indexOfFirstMismatch; - -#if NET - indexOfFirstMismatch = span.CommonPrefixLength(literal); -#else - int minLength = Math.Min(span.Length, literal.Length); - for (indexOfFirstMismatch = 0; indexOfFirstMismatch < minLength; indexOfFirstMismatch++) - { - if (span[indexOfFirstMismatch] != literal[indexOfFirstMismatch]) - { - break; - } - } -#endif + int indexOfFirstMismatch = span.CommonPrefixLength(literal); Debug.Assert(indexOfFirstMismatch >= 0 && indexOfFirstMismatch < literal.Length); @@ -2206,7 +2193,7 @@ private bool SkipOrConsumeCommentMultiSegmentWithRollback() } else { - ValueSpan = commentSequence.First.Span; + ValueSpan = commentSequence.FirstSpan; } if (_tokenType != JsonTokenType.Comment) diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Reader/Utf8JsonReader.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Reader/Utf8JsonReader.cs index 7d7bb3e0010018..0d7f5aadd92d20 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Reader/Utf8JsonReader.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Reader/Utf8JsonReader.cs @@ -666,7 +666,7 @@ private readonly bool UnescapeSequenceAndCompare(ReadOnlySpan other) if (localSequence.IsSingleSegment) { - result = JsonReaderHelper.UnescapeAndCompare(localSequence.First.Span, other); + result = JsonReaderHelper.UnescapeAndCompare(localSequence.FirstSpan, other); } else { diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/EnumConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/EnumConverter.cs index 30002f4e98a099..3e8d79c6ba9148 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/EnumConverter.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/EnumConverter.cs @@ -530,13 +530,8 @@ internal override JsonValueType GetSupportedJsonValueTypes(JsonNumberHandling _) private static EnumFieldInfo[] ResolveEnumFields(JsonNamingPolicy? namingPolicy) { -#if NET string[] names = Enum.GetNames(); T[] values = Enum.GetValues(); -#else - string[] names = Enum.GetNames(typeof(T)); - T[] values = (T[])Enum.GetValues(typeof(T)); -#endif Debug.Assert(names.Length == values.Length); Dictionary? enumMemberAttributes = null; @@ -700,7 +695,7 @@ private static EnumFieldInfo[] TopologicalSortEnumFields(EnumFieldInfo[] enumFie { // We want values with more bits set to come first so negate the pop count. // Keep the index as a second comparand so that sorting stability is preserved. - indices[i] = (-PopCount(enumFields[i].Key), i); + indices[i] = (-(int)ulong.PopCount(enumFields[i].Key), i); } Array.Sort(indices); @@ -716,21 +711,6 @@ private static EnumFieldInfo[] TopologicalSortEnumFields(EnumFieldInfo[] enumFie return sortedFields; } - private static int PopCount(ulong value) - { -#if NET - return (int)ulong.PopCount(value); -#else - int count = 0; - while (value != 0) - { - value &= value - 1; - count++; - } - return count; -#endif - } - private enum EnumFieldNameKind { Default = 0, diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.Utf8JsonReader.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.Utf8JsonReader.cs index 2933c753c7ba69..8a332c69fe3595 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.Utf8JsonReader.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.Utf8JsonReader.cs @@ -439,8 +439,8 @@ private static Utf8JsonReader GetReaderScopedToNextValue(ref Utf8JsonReader read valueSequence = originalSequence.Slice(reader.TokenStartIndex, payloadLength); Debug.Assert( - valueSequence.First.Span[0] == (byte)'"', - $"Calculated sequence starts with {valueSequence.First.Span[0]}"); + valueSequence.FirstSpan[0] == (byte)'"', + $"Calculated sequence starts with {valueSequence.FirstSpan[0]}"); Debug.Assert( valueSequence.ToArray()[payloadLength - 1] == (byte)'"', @@ -454,7 +454,7 @@ private static Utf8JsonReader GetReaderScopedToNextValue(ref Utf8JsonReader read default: byte displayByte = reader.HasValueSequence - ? reader.ValueSequence.First.Span[0] + ? reader.ValueSequence.FirstSpan[0] : reader.ValueSpan[0]; ThrowHelper.ThrowJsonReaderException( diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonTypeInfo.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonTypeInfo.cs index 271e618be80413..2cd721da559461 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonTypeInfo.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonTypeInfo.cs @@ -1666,7 +1666,7 @@ internal static void ValidateType(Type type) internal static bool IsInvalidForSerialization(Type type) { - return type == typeof(void) || type.IsPointer || type.IsByRef || IsByRefLike(type) || type.ContainsGenericParameters; + return type == typeof(void) || type.IsPointer || type.IsByRef || type.IsByRefLike || type.ContainsGenericParameters; } internal void MapInterfaceTypesToCallbacks() @@ -1709,30 +1709,6 @@ internal void SetCreateObjectIfCompatible(Delegate? createObject) } } - private static bool IsByRefLike(Type type) - { -#if NET - return type.IsByRefLike; -#else - if (!type.IsValueType) - { - return false; - } - - object[] attributes = type.GetCustomAttributes(inherit: false); - - for (int i = 0; i < attributes.Length; i++) - { - if (attributes[i].GetType().FullName == "System.Runtime.CompilerServices.IsByRefLikeAttribute") - { - return true; - } - } - - return false; -#endif - } - [DebuggerBrowsable(DebuggerBrowsableState.Never)] internal bool SupportsPolymorphicDeserialization { diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/ReflectionEmitCachingMemberAccessor.Cache.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/ReflectionEmitCachingMemberAccessor.Cache.cs index dbf4642ebf41cb..798b1d26a33ad4 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/ReflectionEmitCachingMemberAccessor.Cache.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/ReflectionEmitCachingMemberAccessor.Cache.cs @@ -30,12 +30,8 @@ public TValue GetOrAdd(TKey key, Func valueFactory) where { CacheEntry entry = _cache.GetOrAdd( key, -#if NET static (TKey key, Func valueFactory) => new(valueFactory(key)), valueFactory); -#else - key => new(valueFactory(key))); -#endif long nowTimestamp = Stopwatch.GetTimestamp(); Volatile.Write(ref entry.LastUsedTimestamp, nowTimestamp); diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/PipeReadBufferState.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/PipeReadBufferState.cs index 45bb1d85351770..dc993405f8a6cf 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/PipeReadBufferState.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/PipeReadBufferState.cs @@ -80,11 +80,7 @@ public void GetReader(JsonReaderState jsonReaderState, out Utf8JsonReader reader if (_sequence.IsSingleSegment) { reader = new Utf8JsonReader( -#if NET _sequence.FirstSpan, -#else - _sequence.First.Span, -#endif IsFinalBlock, jsonReaderState); } else @@ -104,7 +100,7 @@ private void ProcessReadBytes() { if (_sequence.First.Length >= JsonConstants.Utf8Bom.Length) { - if (_sequence.First.Span.StartsWith(JsonConstants.Utf8Bom)) + if (_sequence.FirstSpan.StartsWith(JsonConstants.Utf8Bom)) { _sequence = _sequence.Slice((byte)JsonConstants.Utf8Bom.Length); } diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/JsonNode/JsonArrayTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/JsonNode/JsonArrayTests.cs index 368e05c478dd39..8b89286b39237f 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/JsonNode/JsonArrayTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/JsonNode/JsonArrayTests.cs @@ -31,6 +31,32 @@ void Verify(JsonArray jsonArray, JsonNodeOptions? expectedOptions) } } + [Theory] + [InlineData(false)] + [InlineData(true)] + public static void SpanConstructor_CopiesSliceAndAssignsParents(bool withOptions) + { + JsonNode?[] items = [-1, 1, null, 3, -2]; + ReadOnlySpan span = items.AsSpan(1, 3); + JsonNodeOptions options = new() { PropertyNameCaseInsensitive = true }; + + JsonArray array = withOptions ? new JsonArray(options, span) : new JsonArray(span); + + Assert.Equal(3, array.Count); + Assert.Equal("[1,null,3]", array.ToJsonString()); + Assert.Equal(withOptions ? options : (JsonNodeOptions?)null, array.Options); + Assert.Same(items[1], array[0]); + Assert.Null(array[1]); + Assert.Same(items[3], array[2]); + Assert.Same(array, array[0]?.Parent); + Assert.Same(array, array[2]?.Parent); + Assert.Null(items[0]?.Parent); + Assert.Null(items[4]?.Parent); + + items[1] = 4; + Assert.Equal("[1,null,3]", array.ToJsonString()); + } + [Fact] public static void FromElement() { diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/EnumConverterTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/EnumConverterTests.cs index 3db757e8f23ed7..452e546de5d3cf 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/EnumConverterTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/EnumConverterTests.cs @@ -1413,6 +1413,36 @@ public static void EnumWithOverlappingBitsTests() Assert.Equal("\"BITS01, BIT3\"", json2); } + [Theory] + [InlineData(EnumWithHighBits.None, "\"None\"")] + [InlineData(EnumWithHighBits.Low, "\"Low\"")] + [InlineData(EnumWithHighBits.High, "\"High\"")] + [InlineData(EnumWithHighBits.HighAndLow, "\"HighAndLow\"")] + [InlineData(EnumWithHighBits.High | EnumWithHighBits.Middle, "\"Middle, High\"")] + [InlineData(EnumWithHighBits.All, "\"All\"")] + public static void EnumWithHighBits_Roundtrips(EnumWithHighBits value, string expectedJson) + { + JsonSerializerOptions options = new() { Converters = { new JsonStringEnumConverter() } }; + + string json = JsonSerializer.Serialize(value, options); + + Assert.Equal(expectedJson, json); + Assert.Equal(value, JsonSerializer.Deserialize(json, options)); + } + + [Flags] + public enum EnumWithHighBits : ulong + { + None = 0, + Low = 1, + Middle = 2, + Pair = Low | Middle, + High = 1UL << 63, + HighAndLow = High | Low, + HighAndPair = High | Pair, + All = ulong.MaxValue, + } + [Flags] public enum EnumWithOverlappingBits { diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/JsonElementTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/JsonElementTests.cs index 30272cc7acb208..a8d4c0b42c0ff1 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/JsonElementTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/JsonElementTests.cs @@ -3,6 +3,7 @@ using System.Buffers; using System.Collections.Generic; +using System.Globalization; using System.IO; using System.Threading; using System.Threading.Tasks; @@ -138,6 +139,31 @@ public static void DeepEquals_EqualValuesReturnTrue(string value1, string value2 Assert.True(JsonElement.DeepEquals(element2, element1)); } + [Theory] + [InlineData(0)] + [InlineData(1)] + [InlineData(15)] + [InlineData(16)] + [InlineData(31)] + [InlineData(32)] + [InlineData(255)] + [InlineData(256)] + public static void DeepEquals_NumberNormalizationBoundaries(int zeroCount) + { + string zeros = new('0', zeroCount); + JsonElement fraction = JsonElement.Parse("0." + zeros + "12345" + zeros); + JsonElement fractionExponent = JsonElement.Parse("12345e-" + (zeroCount + 5).ToString(CultureInfo.InvariantCulture)); + JsonElement differentFraction = JsonElement.Parse("0." + zeros + "12346" + zeros); + JsonElement integral = JsonElement.Parse("12345" + zeros); + JsonElement integralExponent = JsonElement.Parse("12345e" + zeroCount.ToString(CultureInfo.InvariantCulture)); + JsonElement zero = JsonElement.Parse("-0." + zeros + "0"); + + Assert.True(JsonElement.DeepEquals(fraction, fractionExponent)); + Assert.True(JsonElement.DeepEquals(integral, integralExponent)); + Assert.True(JsonElement.DeepEquals(zero, JsonElement.Parse("0"))); + Assert.False(JsonElement.DeepEquals(fraction, differentFraction)); + } + [Theory] // Kind mismatch [InlineData("null", "false")] diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/NamingPolicyUnitTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/NamingPolicyUnitTests.cs index 91ae27fe40d2f2..3cd5e646f83656 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/NamingPolicyUnitTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/NamingPolicyUnitTests.cs @@ -64,6 +64,21 @@ public static void CamelCaseNullNameReturnsNull() Assert.Null(policy.ConvertName(null)); } + [Theory] + [InlineData(1)] + [InlineData(2)] + [InlineData(31)] + [InlineData(256)] + [InlineData(1024)] + public static void CamelCase_CopiesEntireName(int prefixLength) + { + const string Suffix = "Value\0\ud800"; + string name = new string('A', prefixLength) + Suffix; + string expected = new string('a', prefixLength) + Suffix; + + Assert.Equal(expected, JsonNamingPolicy.CamelCase.ConvertName(name)); + } + [Theory, OuterLoop] [MemberData(nameof(GetValidMemberNames))] public static void CamelCaseNamingPolicyMatchesNewtonsoftNamingStrategy(string name) diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Utf8JsonReaderTests.MultiSegment.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Utf8JsonReaderTests.MultiSegment.cs index d58ae54e66c6a7..43389785d987a9 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Utf8JsonReaderTests.MultiSegment.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Utf8JsonReaderTests.MultiSegment.cs @@ -37,6 +37,39 @@ public static void InitialStateMultiSegment() Assert.False(json.Read()); } + [Theory] + [InlineData(false, false)] + [InlineData(false, true)] + [InlineData(true, false)] + [InlineData(true, true)] + public static void ReadSequence_WithEmptyBoundarySegments(bool prependEmpty, bool appendEmpty) + { + ReadOnlyMemory data = """{"value":"\u0041"}"""u8.ToArray(); + var firstSegment = new BufferSegment(prependEmpty ? ReadOnlyMemory.Empty : data); + BufferSegment lastSegment = prependEmpty ? firstSegment.Append(data) : firstSegment; + + if (appendEmpty) + { + lastSegment = lastSegment.Append(ReadOnlyMemory.Empty); + } + + var sequence = new ReadOnlySequence(firstSegment, 0, lastSegment, lastSegment.Memory.Length); + var reader = new Utf8JsonReader(sequence); + + Assert.True(reader.Read()); + Assert.Equal(JsonTokenType.StartObject, reader.TokenType); + Assert.True(reader.Read()); + Assert.Equal(JsonTokenType.PropertyName, reader.TokenType); + Assert.True(reader.ValueTextEquals("value"u8)); + Assert.True(reader.Read()); + Assert.Equal(JsonTokenType.String, reader.TokenType); + Assert.True(reader.ValueTextEquals("A"u8)); + Assert.True(reader.Read()); + Assert.Equal(JsonTokenType.EndObject, reader.TokenType); + Assert.False(reader.Read()); + Assert.Equal(data.Length, reader.BytesConsumed); + } + [Fact] public static void EmptyJsonMultiSegmentIsInvalid() { From 864ecfb243edbee3a7c2b8900120c0f39892a94d Mon Sep 17 00:00:00 2001 From: Eirik Tsarpalis Date: Wed, 21 Oct 2026 18:10:05 +0300 Subject: [PATCH 2/9] Remove unused assignment exposed by STJ polyfill cleanup Remove the unused nDigits temporary from AreEqualJsonNumbers while retaining the length comparison. This fixes IDE0059 in analyzer-enabled builds. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 3c692d8a-03a2-4ed9-b337-2066c25a3cb6 --- .../System.Text.Json/src/System/Text/Json/JsonHelpers.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/JsonHelpers.cs b/src/libraries/System.Text.Json/src/System/Text/Json/JsonHelpers.cs index 4fd89bb6411ee3..f4566c2d67c16d 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/JsonHelpers.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/JsonHelpers.cs @@ -336,11 +336,9 @@ public static bool AreEqualJsonNumbers(ReadOnlySpan left, ReadOnlySpan rightFractional, out int rightExponent); - int nDigits; if (leftIsNegative != rightIsNegative || leftExponent != rightExponent || - (nDigits = (leftIntegral.Length + leftFractional.Length)) != - rightIntegral.Length + rightFractional.Length) + leftIntegral.Length + leftFractional.Length != rightIntegral.Length + rightFractional.Length) { return false; } From ffc8253b5f3f8a89f1ecd7bdb70cd68146f55993 Mon Sep 17 00:00:00 2001 From: Eirik Tsarpalis Date: Wed, 21 Oct 2026 18:13:59 +0300 Subject: [PATCH 3/9] Use ThrowIfNull for remaining STJ argument checks Replace five manual null guards and remove the unused shared ThrowHelper. Preserve exception parameter names and writer disposal-check precedence, with public API coverage. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 3c692d8a-03a2-4ed9-b337-2066c25a3cb6 --- .../System.Text.Json/Common/ThrowHelper.cs | 16 -------------- .../System.Text.Json.SourceGeneration.targets | 1 - .../src/System.Text.Json.csproj | 1 - .../Text/Json/Nodes/JsonObject.IDictionary.cs | 6 ++--- .../Attributes/JsonNamingPolicyAttribute.cs | 5 +---- .../Text/Json/Writer/JsonWriterHelper.cs | 3 +-- .../System/Text/Json/Writer/Utf8JsonWriter.cs | 9 ++++---- .../JsonNode/JsonObjectTests.cs | 16 ++++++++++++++ .../JsonWriterOptionsTests.cs | 9 ++++++++ .../Serialization/PropertyNameTests.cs | 15 +++++++++++++ .../Utf8JsonWriterTests.cs | 22 +++++++++++++++++++ 11 files changed, 70 insertions(+), 33 deletions(-) delete mode 100644 src/libraries/System.Text.Json/Common/ThrowHelper.cs diff --git a/src/libraries/System.Text.Json/Common/ThrowHelper.cs b/src/libraries/System.Text.Json/Common/ThrowHelper.cs deleted file mode 100644 index 3f381ce3be6818..00000000000000 --- a/src/libraries/System.Text.Json/Common/ThrowHelper.cs +++ /dev/null @@ -1,16 +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.Diagnostics.CodeAnalysis; - -namespace System.Text.Json -{ - internal static partial class ThrowHelper - { - [DoesNotReturn] - public static void ThrowArgumentNullException(string parameterName) - { - throw new ArgumentNullException(parameterName); - } - } -} diff --git a/src/libraries/System.Text.Json/gen/System.Text.Json.SourceGeneration.targets b/src/libraries/System.Text.Json/gen/System.Text.Json.SourceGeneration.targets index eab16791f1f7c4..31b9e330184767 100644 --- a/src/libraries/System.Text.Json/gen/System.Text.Json.SourceGeneration.targets +++ b/src/libraries/System.Text.Json/gen/System.Text.Json.SourceGeneration.targets @@ -68,7 +68,6 @@ - diff --git a/src/libraries/System.Text.Json/src/System.Text.Json.csproj b/src/libraries/System.Text.Json/src/System.Text.Json.csproj index 929933033c4662..9fb6ad672df7bc 100644 --- a/src/libraries/System.Text.Json/src/System.Text.Json.csproj +++ b/src/libraries/System.Text.Json/src/System.Text.Json.csproj @@ -50,7 +50,6 @@ The System.Text.Json library is built-in as part of the shared framework in .NET - diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Nodes/JsonObject.IDictionary.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Nodes/JsonObject.IDictionary.cs index 8170301c4011bf..5e06067c8ed58f 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Nodes/JsonObject.IDictionary.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Nodes/JsonObject.IDictionary.cs @@ -54,10 +54,8 @@ public void Add(string propertyName, JsonNode? value) /// public bool TryAdd(string propertyName, JsonNode? value, out int index) { - if (propertyName is null) - { - ThrowHelper.ThrowArgumentNullException(nameof(propertyName)); - } + ArgumentNullException.ThrowIfNull(propertyName); + bool success = Dictionary.TryAdd(propertyName, value, out index); if (success) { diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Attributes/JsonNamingPolicyAttribute.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Attributes/JsonNamingPolicyAttribute.cs index d07f860f9ad2d8..caf027b23f594c 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Attributes/JsonNamingPolicyAttribute.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Attributes/JsonNamingPolicyAttribute.cs @@ -43,10 +43,7 @@ public JsonNamingPolicyAttribute(JsonKnownNamingPolicy namingPolicy) /// protected JsonNamingPolicyAttribute(JsonNamingPolicy namingPolicy) { - if (namingPolicy is null) - { - ThrowHelper.ThrowArgumentNullException(nameof(namingPolicy)); - } + ArgumentNullException.ThrowIfNull(namingPolicy); NamingPolicy = namingPolicy; } diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Writer/JsonWriterHelper.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Writer/JsonWriterHelper.cs index 9504b30e27a4d7..5ebaae072f1419 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Writer/JsonWriterHelper.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Writer/JsonWriterHelper.cs @@ -39,8 +39,7 @@ public static void WriteIndentation(Span buffer, int indent, byte indentBy [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void ValidateNewLine(string value) { - if (value is null) - ThrowHelper.ThrowArgumentNullException(nameof(value)); + ArgumentNullException.ThrowIfNull(value); if (value is not JsonConstants.NewLineLineFeed and not JsonConstants.NewLineCarriageReturnLineFeed) ThrowHelper.ThrowArgumentOutOfRangeException_NewLine(nameof(value)); diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.cs index a03476fecd9c74..2fa0eef802d1a3 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.cs @@ -299,10 +299,7 @@ public void Reset(Stream utf8Json) { CheckNotDisposed(); - if (utf8Json is null) - { - throw new ArgumentNullException(nameof(utf8Json)); - } + ArgumentNullException.ThrowIfNull(utf8Json); if (!utf8Json.CanWrite) { @@ -362,7 +359,9 @@ public void Reset(IBufferWriter bufferWriter) { CheckNotDisposed(); - _output = bufferWriter ?? throw new ArgumentNullException(nameof(bufferWriter)); + ArgumentNullException.ThrowIfNull(bufferWriter); + + _output = bufferWriter; _stream = null; _arrayBufferWriter = null; diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/JsonNode/JsonObjectTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/JsonNode/JsonObjectTests.cs index f9d01d456c4d9c..0014b4c9efb039 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/JsonNode/JsonObjectTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/JsonNode/JsonObjectTests.cs @@ -1656,6 +1656,22 @@ public static void TryAdd_ThrowsArgumentNullException() Assert.Throws(() => { jObject.TryAdd(null, 33); }); } + [Theory] + [InlineData(false)] + [InlineData(true)] + public static void TryAdd_NullPropertyNamePreservesState(bool reportIndex) + { + JsonObject jsonObject = new() { ["existing"] = 1 }; + JsonNode value = JsonValue.Create(2); + Action add = reportIndex + ? () => jsonObject.TryAdd(propertyName: null, value, out _) + : () => jsonObject.TryAdd(propertyName: null, value); + + Assert.Throws("propertyName", add); + Assert.Equal("""{"existing":1}""", jsonObject.ToJsonString()); + Assert.Null(value.Parent); + } + [Fact] public static void TryGetPropertyValue_ThrowsArgumentNullException() { diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/JsonWriterOptionsTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/JsonWriterOptionsTests.cs index 5c742d6294b689..09ae951d57bc44 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/JsonWriterOptionsTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/JsonWriterOptionsTests.cs @@ -193,5 +193,14 @@ public static void JsonWriterOptions_NewLine_Null_Throws() var options = new JsonWriterOptions(); Assert.Throws(() => options.NewLine = null); } + + [Fact] + public static void JsonWriterOptions_NewLine_NullPreservesValue() + { + JsonWriterOptions options = new() { NewLine = "\r\n" }; + + Assert.Throws("value", () => options.NewLine = null); + Assert.Equal("\r\n", options.NewLine); + } } } diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/PropertyNameTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/PropertyNameTests.cs index fc621891302f2d..03312914f28162 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/PropertyNameTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/PropertyNameTests.cs @@ -101,5 +101,20 @@ public async Task JsonNamingPolicyAttribute_CustomDerived_TypeLevel_OverridesGlo string json = await Serializer.SerializeWrapper(new ClassWithCustomDerivedNamingPolicyAttribute { MyValue = "test" }, options); Assert.Equal("""{"MYVALUE":"test"}""", json); } + + [Fact] + public void JsonNamingPolicyAttribute_CustomPolicy_ValidatesArgument() + { + Assert.Throws("namingPolicy", () => new CustomNamingPolicyAttribute(null)); + + JsonNamingPolicy namingPolicy = JsonNamingPolicy.CamelCase; + var attribute = new CustomNamingPolicyAttribute(namingPolicy); + Assert.Same(namingPolicy, attribute.NamingPolicy); + } + + private sealed class CustomNamingPolicyAttribute : JsonNamingPolicyAttribute + { + public CustomNamingPolicyAttribute(JsonNamingPolicy namingPolicy) : base(namingPolicy) { } + } } } diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Utf8JsonWriterTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Utf8JsonWriterTests.cs index e0828b3e97784b..d86bbdbdd74677 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Utf8JsonWriterTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Utf8JsonWriterTests.cs @@ -1200,6 +1200,28 @@ public void InvalidReset(JsonWriterOptions options) Assert.Throws(() => writeToIBW.Reset(stream, options)); } + [Theory] + [InlineData(false, false)] + [InlineData(false, true)] + [InlineData(true, false)] + [InlineData(true, true)] + public static void Reset_NullOutputPreservesValidationOrder(bool useStream, bool withOptions) + { + using var writer = new Utf8JsonWriter(new FixedSizedBufferWriter(256)); + Action reset = (useStream, withOptions) switch + { + (true, true) => () => writer.Reset((Stream)null, default), + (true, false) => () => writer.Reset((Stream)null), + (false, true) => () => writer.Reset((IBufferWriter)null, default), + (false, false) => () => writer.Reset((IBufferWriter)null), + }; + + Assert.Throws(useStream ? "utf8Json" : "bufferWriter", reset); + + writer.Dispose(); + Assert.Throws(reset); + } + [Fact] public static void JsonSerializerCacheNotPoisonedByDisposedWriter() { From b05a955fef5855e8bf9e9f1a613b0944e3b4f8d4 Mon Sep 17 00:00:00 2001 From: Eirik Tsarpalis Date: Wed, 21 Oct 2026 22:23:03 +0300 Subject: [PATCH 4/9] Consolidate compatibility polyfills across libraries Replace duplicated compatibility helpers and conditional branches with framework-shaped calls. Reuse the polyfills in existing CBOR tests and shared assertions without adding test coverage. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 3c692d8a-03a2-4ed9-b337-2066c25a3cb6 --- .../ParameterDefaultValue.cs | 9 --- .../Common/src/Polyfills/ArrayPolyfills.cs | 17 +++++ ...ryPrimitivesPolyfills.ReverseEndianness.cs | 38 +++++++++++ .../Polyfills/BinaryPrimitivesPolyfills.cs | 29 +++++++++ .../src/Polyfills/BitConverterPolyfills.cs | 6 ++ .../src/Polyfills/DateTimeOffsetPolyfills.cs | 6 +- .../Common/src/Polyfills/MathPolyfills.cs | 27 ++++++++ .../src/Polyfills/OperatingSystemPolyfills.cs | 22 +++++++ .../src/Polyfills/RuntimeHelpersPolyfills.cs | 4 +- .../src/System/Text/ValueStringBuilder.cs | 4 +- .../TestUtilities/System/AssertExtensions.cs | 23 +++---- .../tests/TestUtilities/TestUtilities.csproj | 2 + ...oft.Extensions.Configuration.Binder.csproj | 1 + ....Configuration.EnvironmentVariables.csproj | 4 ++ ...ns.DependencyInjection.Abstractions.csproj | 1 + .../src/EnvironmentWrapper.cs | 10 +-- ...icrosoft.Extensions.DependencyModel.csproj | 1 + .../DotNetReferenceAssembliesPathResolver.cs | 9 +-- .../MetricListenerConfigurationFactory.cs | 3 +- .../Configuration/MetricsConfiguration.cs | 3 +- .../Configuration/MetricsConfigureOptions.cs | 3 +- ...ultActivityListenerConfigurationFactory.cs | 3 +- .../Configuration/TracingConfiguration.cs | 3 +- .../Configuration/TracingConfigureOptions.cs | 3 +- ...rosoft.Extensions.Diagnostics.Tests.csproj | 5 ++ ....Extensions.Hosting.WindowsServices.csproj | 1 + .../src/WindowsServiceHelpers.cs | 11 +--- .../src/HostingHostBuilderExtensions.cs | 17 +---- .../src/Microsoft.Extensions.Hosting.csproj | 1 + .../src/Microsoft.Extensions.Http.csproj | 1 + .../src/ValueStopwatch.cs | 7 +- ...oft.Extensions.Logging.Abstractions.csproj | 1 + .../src/ConsoleLoggerProvider.cs | 9 +-- ...icrosoft.Extensions.Logging.Console.csproj | 1 + .../Microsoft.Extensions.Primitives.csproj | 1 + .../Immutable/ImmutableArray_1.Builder.cs | 16 ----- .../System/Composition/ExportFactoryOfT.cs | 3 +- .../Hosting/Core/CompositionContract.cs | 4 +- .../Composition/Runtime/Util/Formatters.cs | 18 +++--- .../Hosting/ContainerConfiguration.cs | 2 +- .../TypedParts/Util/DirectAttributeContext.cs | 6 +- .../src/System/Diagnostics/Activity.cs | 6 +- .../src/System/Diagnostics/ActivitySource.cs | 3 +- .../Diagnostics/ActivitySourceOptions.cs | 9 ++- .../Diagnostics/ActivityTagsCollection.cs | 5 +- .../DistributedContextPropagator.cs | 3 +- .../System/Diagnostics/Metrics/Instrument.cs | 6 +- .../src/System/Diagnostics/Metrics/Meter.cs | 3 +- .../Diagnostics/Metrics/MeterOptions.cs | 6 +- .../Diagnostics/Metrics/ObservableCounter.cs | 9 ++- .../Diagnostics/Metrics/ObservableGauge.cs | 9 ++- .../Metrics/ObservableUpDownCounter.cs | 9 ++- .../src/System.Formats.Cbor.csproj | 5 ++ .../Formats/Cbor/CborHelpers.netcoreapp.cs | 25 -------- .../Formats/Cbor/CborHelpers.netstandard.cs | 64 ------------------- .../Formats/Cbor/HalfHelpers.netstandard.cs | 8 +-- .../Formats/Cbor/Reader/CborReader.Simple.cs | 6 +- .../Formats/Cbor/Reader/CborReader.String.cs | 2 +- .../Formats/Cbor/Reader/CborReader.Tag.cs | 2 +- .../Formats/Cbor/Writer/CborWriter.Simple.cs | 5 +- .../Writer/CborWriter.Simple.netstandard.cs | 2 +- .../Formats/Cbor/Writer/CborWriter.Tag.cs | 4 +- .../tests/CborTestHelpers.netcoreapp.cs | 13 ---- .../tests/CborTestHelpers.netstandard.cs | 14 ---- .../tests/Reader/CborReaderTests.Tag.cs | 2 +- .../tests/System.Formats.Cbor.Tests.csproj | 4 +- .../tests/Writer/CborWriterTests.Simple.cs | 2 +- .../tests/Writer/CborWriterTests.Tag.cs | 2 +- .../src/System.Formats.Nrbf.csproj | 1 + .../Nrbf/ArraySinglePrimitiveRecord.cs | 21 ------ .../src/System.IO.Hashing.csproj | 1 + .../src/System/IO/Hashing/XxHash128.cs | 6 +- .../src/System/IO/Hashing/XxHashShared.cs | 20 +----- .../IO/Packaging/IgnoreFlushAndCloseStream.cs | 3 +- .../InterleavedZipPackagePartStream.cs | 7 -- .../Packaging/PackUriHelper.PackUriScheme.cs | 6 +- .../src/System/IO/Packaging/Package.cs | 24 +++---- .../src/System/IO/Packaging/PackagePart.cs | 9 +-- .../src/System/IO/Packaging/ZipPackage.cs | 3 +- .../Net/ServerSentEvents/SseParser_1.cs | 4 -- .../src/System.Reflection.Metadata.csproj | 2 + .../Reflection/Metadata/BlobWriterImpl.cs | 16 ----- .../src/System.Text.Encodings.Web.csproj | 1 + .../JsonNode/JsonArrayTests.cs | 26 -------- .../JsonNode/JsonObjectTests.cs | 16 ----- .../JsonWriterOptionsTests.cs | 9 --- .../Serialization/EnumConverterTests.cs | 30 --------- .../Serialization/JsonElementTests.cs | 26 -------- .../Serialization/NamingPolicyUnitTests.cs | 15 ----- .../Serialization/PropertyNameTests.cs | 15 ----- .../Utf8JsonReaderTests.MultiSegment.cs | 33 ---------- .../Utf8JsonWriterTests.cs | 22 ------- ...m.Text.RegularExpressions.Generator.csproj | 4 ++ .../src/Base/DataflowBlock.cs | 13 ++-- .../src/Base/DataflowBlockOptions.cs | 4 +- .../src/Blocks/BatchBlock.cs | 2 +- .../src/Blocks/BatchedJoinBlock.cs | 4 +- .../src/Blocks/BroadcastBlock.cs | 6 +- .../src/Blocks/TransformBlock.cs | 1 - .../src/Blocks/TransformManyBlock.cs | 6 +- .../src/Blocks/WriteOnceBlock.cs | 6 +- .../src/Internal/SourceCore.cs | 6 +- .../ILLink.RoslynAnalyzer.csproj | 1 + 103 files changed, 340 insertions(+), 592 deletions(-) create mode 100644 src/libraries/Common/src/Polyfills/BinaryPrimitivesPolyfills.ReverseEndianness.cs create mode 100644 src/libraries/Common/src/Polyfills/BinaryPrimitivesPolyfills.cs create mode 100644 src/libraries/Common/src/Polyfills/MathPolyfills.cs create mode 100644 src/libraries/Common/src/Polyfills/OperatingSystemPolyfills.cs delete mode 100644 src/libraries/System.Formats.Cbor/tests/CborTestHelpers.netcoreapp.cs delete mode 100644 src/libraries/System.Formats.Cbor/tests/CborTestHelpers.netstandard.cs diff --git a/src/libraries/Common/src/Extensions/ParameterDefaultValue/ParameterDefaultValue.cs b/src/libraries/Common/src/Extensions/ParameterDefaultValue/ParameterDefaultValue.cs index 105da2aecce890..25446d51980663 100644 --- a/src/libraries/Common/src/Extensions/ParameterDefaultValue/ParameterDefaultValue.cs +++ b/src/libraries/Common/src/Extensions/ParameterDefaultValue/ParameterDefaultValue.cs @@ -4,12 +4,7 @@ using System; using System.Diagnostics.CodeAnalysis; using System.Reflection; - -#if NETFRAMEWORK || NETSTANDARD2_0 -using System.Runtime.Serialization; -#else using System.Runtime.CompilerServices; -#endif namespace Microsoft.Extensions.Internal { @@ -40,11 +35,7 @@ public static bool TryGetDefaultValue(ParameterInfo parameter, out object? defau [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2067:UnrecognizedReflectionPattern", Justification = "CreateValueType is only called on a ValueType. You can always create an instance of a ValueType.")] static object? CreateValueType(Type t) => -#if NETFRAMEWORK || NETSTANDARD2_0 - FormatterServices.GetUninitializedObject(t); -#else RuntimeHelpers.GetUninitializedObject(t); -#endif // Handle nullable enums if (defaultValue != null && isNullableParameterType) diff --git a/src/libraries/Common/src/Polyfills/ArrayPolyfills.cs b/src/libraries/Common/src/Polyfills/ArrayPolyfills.cs index b0b31c47b5a0bb..14dd0ccb9fbf2c 100644 --- a/src/libraries/Common/src/Polyfills/ArrayPolyfills.cs +++ b/src/libraries/Common/src/Polyfills/ArrayPolyfills.cs @@ -10,6 +10,23 @@ internal static class ArrayPolyfills { public static int MaxLength => 0x7FFFFFC7; + public static void Reverse(T[] array, int index, int length) + { + if (array is null) + { + throw new ArgumentNullException(nameof(array)); + } + + if (index < 0 || length < 0 || array.Length - index < length) + { + // Preserve the existing overload's argument validation. + Array.Reverse(array, index, length); + return; + } + + array.AsSpan(index, length).Reverse(); + } + public static void Clear(Array array) { if (array is null) diff --git a/src/libraries/Common/src/Polyfills/BinaryPrimitivesPolyfills.ReverseEndianness.cs b/src/libraries/Common/src/Polyfills/BinaryPrimitivesPolyfills.ReverseEndianness.cs new file mode 100644 index 00000000000000..dff94a7c952055 --- /dev/null +++ b/src/libraries/Common/src/Polyfills/BinaryPrimitivesPolyfills.ReverseEndianness.cs @@ -0,0 +1,38 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Buffers.Binary; + +/// Provides downlevel polyfills for reversing the endianness of spans. +internal static partial class BinaryPrimitivesPolyfills +{ + extension(BinaryPrimitives) + { + public static void ReverseEndianness(ReadOnlySpan source, Span destination) + { + source.CopyTo(destination); + for (int i = 0; i < source.Length; i++) + { + destination[i] = BinaryPrimitives.ReverseEndianness(destination[i]); + } + } + + public static void ReverseEndianness(ReadOnlySpan source, Span destination) + { + source.CopyTo(destination); + for (int i = 0; i < source.Length; i++) + { + destination[i] = BinaryPrimitives.ReverseEndianness(destination[i]); + } + } + + public static void ReverseEndianness(ReadOnlySpan source, Span destination) + { + source.CopyTo(destination); + for (int i = 0; i < source.Length; i++) + { + destination[i] = BinaryPrimitives.ReverseEndianness(destination[i]); + } + } + } +} diff --git a/src/libraries/Common/src/Polyfills/BinaryPrimitivesPolyfills.cs b/src/libraries/Common/src/Polyfills/BinaryPrimitivesPolyfills.cs new file mode 100644 index 00000000000000..94f934d2a6d0f4 --- /dev/null +++ b/src/libraries/Common/src/Polyfills/BinaryPrimitivesPolyfills.cs @@ -0,0 +1,29 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Buffers.Binary; + +/// Provides downlevel polyfills for static methods on . +internal static partial class BinaryPrimitivesPolyfills +{ + extension(BinaryPrimitives) + { + public static float ReadSingleBigEndian(ReadOnlySpan source) => + BitConverter.Int32BitsToSingle(BinaryPrimitives.ReadInt32BigEndian(source)); + + public static double ReadDoubleBigEndian(ReadOnlySpan source) => + BitConverter.Int64BitsToDouble(BinaryPrimitives.ReadInt64BigEndian(source)); + + public static void WriteSingleBigEndian(Span destination, float value) => + BinaryPrimitives.WriteInt32BigEndian(destination, BitConverter.SingleToInt32Bits(value)); + + public static void WriteDoubleBigEndian(Span destination, double value) => + BinaryPrimitives.WriteInt64BigEndian(destination, BitConverter.DoubleToInt64Bits(value)); + + public static void WriteSingleLittleEndian(Span destination, float value) => + BinaryPrimitives.WriteInt32LittleEndian(destination, BitConverter.SingleToInt32Bits(value)); + + public static void WriteDoubleLittleEndian(Span destination, double value) => + BinaryPrimitives.WriteInt64LittleEndian(destination, BitConverter.DoubleToInt64Bits(value)); + } +} diff --git a/src/libraries/Common/src/Polyfills/BitConverterPolyfills.cs b/src/libraries/Common/src/Polyfills/BitConverterPolyfills.cs index 92fed514916fa7..78c2930f664169 100644 --- a/src/libraries/Common/src/Polyfills/BitConverterPolyfills.cs +++ b/src/libraries/Common/src/Polyfills/BitConverterPolyfills.cs @@ -10,6 +10,12 @@ internal static class BitConverterPolyfills { extension(BitConverter) { + public static unsafe int SingleToInt32Bits(float value) => *(int*)&value; + + public static unsafe float Int32BitsToSingle(int value) => *(float*)&value; + + public static float UInt32BitsToSingle(uint value) => BitConverter.Int32BitsToSingle((int)value); + public static uint SingleToUInt32Bits(float value) { unsafe diff --git a/src/libraries/Common/src/Polyfills/DateTimeOffsetPolyfills.cs b/src/libraries/Common/src/Polyfills/DateTimeOffsetPolyfills.cs index 1dcbae615775ad..cf8175659679f9 100644 --- a/src/libraries/Common/src/Polyfills/DateTimeOffsetPolyfills.cs +++ b/src/libraries/Common/src/Polyfills/DateTimeOffsetPolyfills.cs @@ -3,11 +3,15 @@ namespace System; -/// Provides downlevel polyfills for instance members on . +/// Provides downlevel polyfills for members on . internal static class DateTimeOffsetPolyfills { + private static readonly DateTimeOffset s_unixEpoch = new(1970, 1, 1, 0, 0, 0, TimeSpan.Zero); + extension(DateTimeOffset value) { + public static DateTimeOffset UnixEpoch => s_unixEpoch; + public int TotalOffsetMinutes => (int)(value.Offset.Ticks / TimeSpan.TicksPerMinute); } diff --git a/src/libraries/Common/src/Polyfills/MathPolyfills.cs b/src/libraries/Common/src/Polyfills/MathPolyfills.cs new file mode 100644 index 00000000000000..bb89af9dc8dc28 --- /dev/null +++ b/src/libraries/Common/src/Polyfills/MathPolyfills.cs @@ -0,0 +1,27 @@ +// 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; + +namespace System; + +/// Provides downlevel polyfills for static methods on . +internal static class MathPolyfills +{ + extension(Math) + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ulong BigMul(ulong a, ulong b, out ulong low) + { + ulong lowerLow = (ulong)(uint)a * (uint)b; + ulong higherLow = (ulong)(uint)(a >> 32) * (uint)b; + ulong lowerHigh = (ulong)(uint)a * (uint)(b >> 32); + ulong higherHigh = (ulong)(uint)(a >> 32) * (uint)(b >> 32); + + ulong cross = (lowerLow >> 32) + (higherLow & uint.MaxValue) + lowerHigh; + ulong upper = (higherLow >> 32) + (cross >> 32) + higherHigh; + low = (cross << 32) | (lowerLow & uint.MaxValue); + return upper; + } + } +} diff --git a/src/libraries/Common/src/Polyfills/OperatingSystemPolyfills.cs b/src/libraries/Common/src/Polyfills/OperatingSystemPolyfills.cs new file mode 100644 index 00000000000000..ba4a7b54ee2782 --- /dev/null +++ b/src/libraries/Common/src/Polyfills/OperatingSystemPolyfills.cs @@ -0,0 +1,22 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#if !NETFRAMEWORK +using System.Runtime.InteropServices; +#endif + +namespace System; + +/// Provides downlevel polyfills for static methods on . +internal static class OperatingSystemPolyfills +{ + extension(OperatingSystem) + { + public static bool IsWindows() => +#if NETFRAMEWORK + Environment.OSVersion.Platform == PlatformID.Win32NT; +#else + RuntimeInformation.IsOSPlatform(OSPlatform.Windows); +#endif + } +} diff --git a/src/libraries/Common/src/Polyfills/RuntimeHelpersPolyfills.cs b/src/libraries/Common/src/Polyfills/RuntimeHelpersPolyfills.cs index 112cefff5bb742..c1b5a125c919a9 100644 --- a/src/libraries/Common/src/Polyfills/RuntimeHelpersPolyfills.cs +++ b/src/libraries/Common/src/Polyfills/RuntimeHelpersPolyfills.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; namespace System.Runtime.CompilerServices; @@ -24,7 +25,8 @@ public static bool TryEnsureSufficientExecutionStack() } } - public static object GetUninitializedObject(Type type) + public static object GetUninitializedObject( + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)] Type type) { #pragma warning disable SYSLIB0050 // FormatterServices.GetUninitializedObject is obsolete return System.Runtime.Serialization.FormatterServices.GetUninitializedObject(type); diff --git a/src/libraries/Common/src/System/Text/ValueStringBuilder.cs b/src/libraries/Common/src/System/Text/ValueStringBuilder.cs index 75814574852a0d..72095ef1255736 100644 --- a/src/libraries/Common/src/System/Text/ValueStringBuilder.cs +++ b/src/libraries/Common/src/System/Text/ValueStringBuilder.cs @@ -252,13 +252,11 @@ private void Grow(int additionalCapacityBeyondPos) Debug.Assert(additionalCapacityBeyondPos > 0); Debug.Assert(_pos > _chars.Length - additionalCapacityBeyondPos, "Grow called incorrectly, no resize is needed."); - const uint ArrayMaxLength = 0x7FFFFFC7; // same as Array.MaxLength - // Increase to at least the required size (_pos + additionalCapacityBeyondPos), but try // to double the size if possible, bounding the doubling to not go beyond the max array length. int newCapacity = (int)Math.Max( (uint)(_pos + additionalCapacityBeyondPos), - Math.Min((uint)_chars.Length * 2, ArrayMaxLength)); + Math.Min((uint)_chars.Length * 2, (uint)Array.MaxLength)); // Make sure to let Rent throw an exception if the caller has a bug and the desired capacity is negative. // This could also go negative if the actual required length wraps around. diff --git a/src/libraries/Common/tests/TestUtilities/System/AssertExtensions.cs b/src/libraries/Common/tests/TestUtilities/System/AssertExtensions.cs index 6d74a4da5a6080..85a38244ccf535 100644 --- a/src/libraries/Common/tests/TestUtilities/System/AssertExtensions.cs +++ b/src/libraries/Common/tests/TestUtilities/System/AssertExtensions.cs @@ -725,24 +725,24 @@ public ItemCount(int original, int remain) } } - static unsafe bool IsNegativeZero(float value) + static bool IsNegativeZero(float value) { - return (*(uint*)(&value)) == 0x80000000; + return BitConverter.SingleToInt32Bits(value) == int.MinValue; } - static unsafe bool IsPositiveZero(float value) + static bool IsPositiveZero(float value) { - return (*(uint*)(&value)) == 0x00000000; + return BitConverter.SingleToInt32Bits(value) == 0; } - static unsafe bool IsNegativeZero(double value) + static bool IsNegativeZero(double value) { - return (*(ulong*)(&value)) == 0x8000000000000000; + return BitConverter.DoubleToInt64Bits(value) == long.MinValue; } - static unsafe bool IsPositiveZero(double value) + static bool IsPositiveZero(double value) { - return (*(ulong*)(&value)) == 0x0000000000000000; + return BitConverter.DoubleToInt64Bits(value) == 0; } #if NET @@ -1236,12 +1236,7 @@ public static void Equal(double expected, double actual) /// Thrown when the representations are not identical public static void Equal(float expected, float actual) { - static unsafe int SingleToInt32Bits(float value) - { - return *(int*)&value; - } - - if (SingleToInt32Bits(expected) == SingleToInt32Bits(actual)) + if (BitConverter.SingleToInt32Bits(expected) == BitConverter.SingleToInt32Bits(actual)) { return; } diff --git a/src/libraries/Common/tests/TestUtilities/TestUtilities.csproj b/src/libraries/Common/tests/TestUtilities/TestUtilities.csproj index ba8963659c58a8..85eb76b01daab8 100644 --- a/src/libraries/Common/tests/TestUtilities/TestUtilities.csproj +++ b/src/libraries/Common/tests/TestUtilities/TestUtilities.csproj @@ -117,6 +117,8 @@ + + diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/src/Microsoft.Extensions.Configuration.Binder.csproj b/src/libraries/Microsoft.Extensions.Configuration.Binder/src/Microsoft.Extensions.Configuration.Binder.csproj index d60e1edb55f2e5..32932c077a2d57 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/src/Microsoft.Extensions.Configuration.Binder.csproj +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/src/Microsoft.Extensions.Configuration.Binder.csproj @@ -22,6 +22,7 @@ + diff --git a/src/libraries/Microsoft.Extensions.Configuration.EnvironmentVariables/src/Microsoft.Extensions.Configuration.EnvironmentVariables.csproj b/src/libraries/Microsoft.Extensions.Configuration.EnvironmentVariables/src/Microsoft.Extensions.Configuration.EnvironmentVariables.csproj index 39f86fd9651ea2..f2c8d4bcadfeb6 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.EnvironmentVariables/src/Microsoft.Extensions.Configuration.EnvironmentVariables.csproj +++ b/src/libraries/Microsoft.Extensions.Configuration.EnvironmentVariables/src/Microsoft.Extensions.Configuration.EnvironmentVariables.csproj @@ -19,5 +19,9 @@ + + + + diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection.Abstractions/src/Microsoft.Extensions.DependencyInjection.Abstractions.csproj b/src/libraries/Microsoft.Extensions.DependencyInjection.Abstractions/src/Microsoft.Extensions.DependencyInjection.Abstractions.csproj index 95067b13cd7039..59de0f79e08e04 100644 --- a/src/libraries/Microsoft.Extensions.DependencyInjection.Abstractions/src/Microsoft.Extensions.DependencyInjection.Abstractions.csproj +++ b/src/libraries/Microsoft.Extensions.DependencyInjection.Abstractions/src/Microsoft.Extensions.DependencyInjection.Abstractions.csproj @@ -21,6 +21,7 @@ Microsoft.Extensions.DependencyInjection.IServiceCollection + diff --git a/src/libraries/Microsoft.Extensions.DependencyModel/src/EnvironmentWrapper.cs b/src/libraries/Microsoft.Extensions.DependencyModel/src/EnvironmentWrapper.cs index 01510386027730..46629338871128 100644 --- a/src/libraries/Microsoft.Extensions.DependencyModel/src/EnvironmentWrapper.cs +++ b/src/libraries/Microsoft.Extensions.DependencyModel/src/EnvironmentWrapper.cs @@ -2,9 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; -#if !NETFRAMEWORK -using System.Runtime.InteropServices; -#endif namespace Microsoft.Extensions.DependencyModel { @@ -16,11 +13,6 @@ internal sealed class EnvironmentWrapper : IEnvironment public object? GetAppContextData(string name) => AppDomain.CurrentDomain.GetData(name); - public bool IsWindows() => -#if NETFRAMEWORK - Environment.OSVersion.Platform == PlatformID.Win32NT; -#else - RuntimeInformation.IsOSPlatform(OSPlatform.Windows); -#endif + public bool IsWindows() => OperatingSystem.IsWindows(); } } diff --git a/src/libraries/Microsoft.Extensions.DependencyModel/src/Microsoft.Extensions.DependencyModel.csproj b/src/libraries/Microsoft.Extensions.DependencyModel/src/Microsoft.Extensions.DependencyModel.csproj index 1fdc6a3d0aa132..5486492bc49c6e 100644 --- a/src/libraries/Microsoft.Extensions.DependencyModel/src/Microsoft.Extensions.DependencyModel.csproj +++ b/src/libraries/Microsoft.Extensions.DependencyModel/src/Microsoft.Extensions.DependencyModel.csproj @@ -19,6 +19,7 @@ By default, the dependency manifest contains information about the application's + diff --git a/src/libraries/Microsoft.Extensions.DependencyModel/src/Resolution/DotNetReferenceAssembliesPathResolver.cs b/src/libraries/Microsoft.Extensions.DependencyModel/src/Resolution/DotNetReferenceAssembliesPathResolver.cs index ead221a3fb30d4..ba21d26591e2f7 100644 --- a/src/libraries/Microsoft.Extensions.DependencyModel/src/Resolution/DotNetReferenceAssembliesPathResolver.cs +++ b/src/libraries/Microsoft.Extensions.DependencyModel/src/Resolution/DotNetReferenceAssembliesPathResolver.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; #if !NETFRAMEWORK using System.Runtime.InteropServices; #endif @@ -29,13 +30,7 @@ public class DotNetReferenceAssembliesPathResolver private static string? GetDefaultDotNetReferenceAssembliesPath(IFileSystem fileSystem) { - if ( -#if NETFRAMEWORK - System.Environment.OSVersion.Platform == System.PlatformID.Win32NT -#else - RuntimeInformation.IsOSPlatform(OSPlatform.Windows) -#endif - ) + if (OperatingSystem.IsWindows()) { return null; } diff --git a/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/Configuration/MetricListenerConfigurationFactory.cs b/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/Configuration/MetricListenerConfigurationFactory.cs index a08935c47661bf..067e3ccf4572ac 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/Configuration/MetricListenerConfigurationFactory.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/Configuration/MetricListenerConfigurationFactory.cs @@ -13,7 +13,8 @@ internal sealed class MetricListenerConfigurationFactory : IMetricListenerConfig public MetricListenerConfigurationFactory(IEnumerable configurations) { - _configurations = configurations ?? throw new ArgumentNullException(nameof(configurations)); + ArgumentNullException.ThrowIfNull(configurations); + _configurations = configurations; } public IConfiguration GetConfiguration(string listenerName) diff --git a/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/Configuration/MetricsConfiguration.cs b/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/Configuration/MetricsConfiguration.cs index 70f9d3348ffe87..4a40e5e1203cfe 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/Configuration/MetricsConfiguration.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/Configuration/MetricsConfiguration.cs @@ -10,7 +10,8 @@ internal sealed class MetricsConfiguration { public MetricsConfiguration(IConfiguration configuration) { - Configuration = configuration ?? throw new ArgumentNullException(nameof(configuration)); + ArgumentNullException.ThrowIfNull(configuration); + Configuration = configuration; } public IConfiguration Configuration { get; } diff --git a/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/Configuration/MetricsConfigureOptions.cs b/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/Configuration/MetricsConfigureOptions.cs index 9af86bcdde6458..4a4df45ccefa7b 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/Configuration/MetricsConfigureOptions.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/Configuration/MetricsConfigureOptions.cs @@ -18,7 +18,8 @@ internal sealed class MetricsConfigureOptions : IConfigureOptions LoadConfig(options); diff --git a/src/libraries/Microsoft.Extensions.Diagnostics/src/Tracing/Configuration/DefaultActivityListenerConfigurationFactory.cs b/src/libraries/Microsoft.Extensions.Diagnostics/src/Tracing/Configuration/DefaultActivityListenerConfigurationFactory.cs index bf2d9f7c27fb27..669309c794246d 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics/src/Tracing/Configuration/DefaultActivityListenerConfigurationFactory.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics/src/Tracing/Configuration/DefaultActivityListenerConfigurationFactory.cs @@ -13,7 +13,8 @@ internal sealed class DefaultActivityListenerConfigurationFactory : ActivityList public DefaultActivityListenerConfigurationFactory(IEnumerable configurations) { - _configurations = configurations ?? throw new ArgumentNullException(nameof(configurations)); + ArgumentNullException.ThrowIfNull(configurations); + _configurations = configurations; } public override IConfiguration GetConfiguration(string listenerName) diff --git a/src/libraries/Microsoft.Extensions.Diagnostics/src/Tracing/Configuration/TracingConfiguration.cs b/src/libraries/Microsoft.Extensions.Diagnostics/src/Tracing/Configuration/TracingConfiguration.cs index 279388b225cb26..507d1572781f8c 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics/src/Tracing/Configuration/TracingConfiguration.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics/src/Tracing/Configuration/TracingConfiguration.cs @@ -10,7 +10,8 @@ internal sealed class TracingConfiguration { public TracingConfiguration(IConfiguration configuration) { - Configuration = configuration ?? throw new ArgumentNullException(nameof(configuration)); + ArgumentNullException.ThrowIfNull(configuration); + Configuration = configuration; } public IConfiguration Configuration { get; } diff --git a/src/libraries/Microsoft.Extensions.Diagnostics/src/Tracing/Configuration/TracingConfigureOptions.cs b/src/libraries/Microsoft.Extensions.Diagnostics/src/Tracing/Configuration/TracingConfigureOptions.cs index f2f2fb8ede67fb..82cc779ae60cce 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics/src/Tracing/Configuration/TracingConfigureOptions.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics/src/Tracing/Configuration/TracingConfigureOptions.cs @@ -19,7 +19,8 @@ internal sealed class TracingConfigureOptions : IConfigureOptions LoadConfig(options); diff --git a/src/libraries/Microsoft.Extensions.Diagnostics/tests/Microsoft.Extensions.Diagnostics.Tests.csproj b/src/libraries/Microsoft.Extensions.Diagnostics/tests/Microsoft.Extensions.Diagnostics.Tests.csproj index c0368b28c4b6fb..6a4350d65fd269 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics/tests/Microsoft.Extensions.Diagnostics.Tests.csproj +++ b/src/libraries/Microsoft.Extensions.Diagnostics/tests/Microsoft.Extensions.Diagnostics.Tests.csproj @@ -25,4 +25,9 @@ + + + + + diff --git a/src/libraries/Microsoft.Extensions.Hosting.WindowsServices/src/Microsoft.Extensions.Hosting.WindowsServices.csproj b/src/libraries/Microsoft.Extensions.Hosting.WindowsServices/src/Microsoft.Extensions.Hosting.WindowsServices.csproj index a8008eca3475c5..8977f6f2a13056 100644 --- a/src/libraries/Microsoft.Extensions.Hosting.WindowsServices/src/Microsoft.Extensions.Hosting.WindowsServices.csproj +++ b/src/libraries/Microsoft.Extensions.Hosting.WindowsServices/src/Microsoft.Extensions.Hosting.WindowsServices.csproj @@ -26,6 +26,7 @@ + diff --git a/src/libraries/Microsoft.Extensions.Hosting.WindowsServices/src/WindowsServiceHelpers.cs b/src/libraries/Microsoft.Extensions.Hosting.WindowsServices/src/WindowsServiceHelpers.cs index 05eed40ff6df48..e3ee085d716a06 100644 --- a/src/libraries/Microsoft.Extensions.Hosting.WindowsServices/src/WindowsServiceHelpers.cs +++ b/src/libraries/Microsoft.Extensions.Hosting.WindowsServices/src/WindowsServiceHelpers.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; -using System.Runtime.InteropServices; using System.Runtime.Versioning; namespace Microsoft.Extensions.Hosting.WindowsServices @@ -25,15 +24,7 @@ public static class WindowsServiceHelpers private static bool GetIsWindowsService() { - if ( -#if NETFRAMEWORK - Environment.OSVersion.Platform != PlatformID.Win32NT -#elif NET - !OperatingSystem.IsWindows() -#else - !RuntimeInformation.IsOSPlatform(OSPlatform.Windows) -#endif - ) + if (!OperatingSystem.IsWindows()) { return false; } diff --git a/src/libraries/Microsoft.Extensions.Hosting/src/HostingHostBuilderExtensions.cs b/src/libraries/Microsoft.Extensions.Hosting/src/HostingHostBuilderExtensions.cs index d23fd0d7903d74..487eacd6eeb262 100644 --- a/src/libraries/Microsoft.Extensions.Hosting/src/HostingHostBuilderExtensions.cs +++ b/src/libraries/Microsoft.Extensions.Hosting/src/HostingHostBuilderExtensions.cs @@ -6,7 +6,6 @@ using System.Diagnostics.CodeAnalysis; using System.IO; using System.Reflection; -using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Threading; using System.Threading.Tasks; @@ -219,12 +218,7 @@ internal static void SetDefaultContentRoot(IConfigurationBuilder hostConfigBuild // any trailing directory separator characters. I'm not even sure the casing can ever be different from these APIs, but I think it makes sense to // ignore case for Windows path comparisons given the file system is usually (always?) going to be case insensitive for the system path. string cwd = Environment.CurrentDirectory; - if ( -#if NETFRAMEWORK - Environment.OSVersion.Platform != PlatformID.Win32NT || -#else - !RuntimeInformation.IsOSPlatform(OSPlatform.Windows) || -#endif + if (!OperatingSystem.IsWindows() || !string.Equals(cwd, Environment.SystemDirectory, StringComparison.OrdinalIgnoreCase)) { hostConfigBuilder.AddInMemoryCollection(new[] @@ -283,14 +277,7 @@ internal static void AddDefaultServices(HostBuilderContext hostingContext, IServ { services.AddLogging(logging => { - bool isWindows = -#if NET - OperatingSystem.IsWindows(); -#elif NETFRAMEWORK - Environment.OSVersion.Platform == PlatformID.Win32NT; -#else - RuntimeInformation.IsOSPlatform(OSPlatform.Windows); -#endif + bool isWindows = OperatingSystem.IsWindows(); // IMPORTANT: This needs to be added *before* configuration is loaded, this lets // the defaults be overridden by the configuration. diff --git a/src/libraries/Microsoft.Extensions.Hosting/src/Microsoft.Extensions.Hosting.csproj b/src/libraries/Microsoft.Extensions.Hosting/src/Microsoft.Extensions.Hosting.csproj index 3538ba4b7397bd..0e0625db63be55 100644 --- a/src/libraries/Microsoft.Extensions.Hosting/src/Microsoft.Extensions.Hosting.csproj +++ b/src/libraries/Microsoft.Extensions.Hosting/src/Microsoft.Extensions.Hosting.csproj @@ -13,6 +13,7 @@ + diff --git a/src/libraries/Microsoft.Extensions.Http/src/Microsoft.Extensions.Http.csproj b/src/libraries/Microsoft.Extensions.Http/src/Microsoft.Extensions.Http.csproj index 8192e205ca4f84..5942a8fe1268c6 100644 --- a/src/libraries/Microsoft.Extensions.Http/src/Microsoft.Extensions.Http.csproj +++ b/src/libraries/Microsoft.Extensions.Http/src/Microsoft.Extensions.Http.csproj @@ -20,6 +20,7 @@ System.Net.Http.IHttpClientFactory + diff --git a/src/libraries/Microsoft.Extensions.Http/src/ValueStopwatch.cs b/src/libraries/Microsoft.Extensions.Http/src/ValueStopwatch.cs index 1005a485362446..078c464e021105 100644 --- a/src/libraries/Microsoft.Extensions.Http/src/ValueStopwatch.cs +++ b/src/libraries/Microsoft.Extensions.Http/src/ValueStopwatch.cs @@ -8,8 +8,6 @@ namespace Microsoft.Extensions.Internal { internal readonly struct ValueStopwatch { - private static readonly double s_timestampToTicks = TimeSpan.TicksPerSecond / (double)Stopwatch.Frequency; - private readonly long _startTimestamp; public bool IsActive => _startTimestamp != 0; @@ -30,10 +28,7 @@ public TimeSpan GetElapsedTime() throw new InvalidOperationException("An uninitialized, or 'default', ValueStopwatch cannot be used to get elapsed time."); } - long end = Stopwatch.GetTimestamp(); - long timestampDelta = end - _startTimestamp; - long ticks = (long)(s_timestampToTicks * timestampDelta); - return new TimeSpan(ticks); + return Stopwatch.GetElapsedTime(_startTimestamp); } } } diff --git a/src/libraries/Microsoft.Extensions.Logging.Abstractions/src/Microsoft.Extensions.Logging.Abstractions.csproj b/src/libraries/Microsoft.Extensions.Logging.Abstractions/src/Microsoft.Extensions.Logging.Abstractions.csproj index acc645ed453fd9..e6fee2f75e5434 100644 --- a/src/libraries/Microsoft.Extensions.Logging.Abstractions/src/Microsoft.Extensions.Logging.Abstractions.csproj +++ b/src/libraries/Microsoft.Extensions.Logging.Abstractions/src/Microsoft.Extensions.Logging.Abstractions.csproj @@ -32,6 +32,7 @@ Microsoft.Extensions.Logging.Abstractions.NullLogger + diff --git a/src/libraries/Microsoft.Extensions.Logging.Console/src/ConsoleLoggerProvider.cs b/src/libraries/Microsoft.Extensions.Logging.Console/src/ConsoleLoggerProvider.cs index 03796a6dff92d1..8bd2a7966a8ca1 100644 --- a/src/libraries/Microsoft.Extensions.Logging.Console/src/ConsoleLoggerProvider.cs +++ b/src/libraries/Microsoft.Extensions.Logging.Console/src/ConsoleLoggerProvider.cs @@ -5,7 +5,6 @@ using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; -using System.Runtime.InteropServices; using System.Runtime.Versioning; using Microsoft.Extensions.Options; @@ -73,13 +72,7 @@ private static bool DoesConsoleSupportAnsi() return colorOverride; } - if ( -#if NETFRAMEWORK - Environment.OSVersion.Platform != PlatformID.Win32NT -#else - !RuntimeInformation.IsOSPlatform(OSPlatform.Windows) -#endif - ) + if (!OperatingSystem.IsWindows()) { return true; } diff --git a/src/libraries/Microsoft.Extensions.Logging.Console/src/Microsoft.Extensions.Logging.Console.csproj b/src/libraries/Microsoft.Extensions.Logging.Console/src/Microsoft.Extensions.Logging.Console.csproj index 8bfa0c8da677f9..a414536f475924 100644 --- a/src/libraries/Microsoft.Extensions.Logging.Console/src/Microsoft.Extensions.Logging.Console.csproj +++ b/src/libraries/Microsoft.Extensions.Logging.Console/src/Microsoft.Extensions.Logging.Console.csproj @@ -32,6 +32,7 @@ + diff --git a/src/libraries/Microsoft.Extensions.Primitives/src/Microsoft.Extensions.Primitives.csproj b/src/libraries/Microsoft.Extensions.Primitives/src/Microsoft.Extensions.Primitives.csproj index 47607b70e61fad..8c3b0115ab024f 100644 --- a/src/libraries/Microsoft.Extensions.Primitives/src/Microsoft.Extensions.Primitives.csproj +++ b/src/libraries/Microsoft.Extensions.Primitives/src/Microsoft.Extensions.Primitives.csproj @@ -26,6 +26,7 @@ Microsoft.Extensions.Primitives.StringSegment + diff --git a/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableArray_1.Builder.cs b/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableArray_1.Builder.cs index 5d414f4249796c..a4d8c7f7ce9015 100644 --- a/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableArray_1.Builder.cs +++ b/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableArray_1.Builder.cs @@ -914,23 +914,7 @@ public int LastIndexOf(T item, int startIndex, int count, IEqualityComparer? /// public void Reverse() { -#if NET || NETSTANDARD2_1_OR_GREATER Array.Reverse(_elements, 0, _count); -#else - // The non-generic Array.Reverse is not used because it does not perform - // well for non-primitive value types. - int i = 0; - int j = _count - 1; - T[] array = _elements; - while (i < j) - { - T temp = array[i]; - array[i] = array[j]; - array[j] = temp; - i++; - j--; - } -#endif } /// diff --git a/src/libraries/System.Composition.Runtime/src/System/Composition/ExportFactoryOfT.cs b/src/libraries/System.Composition.Runtime/src/System/Composition/ExportFactoryOfT.cs index dabcc4491b5ad6..595e74a2aa1e9a 100644 --- a/src/libraries/System.Composition.Runtime/src/System/Composition/ExportFactoryOfT.cs +++ b/src/libraries/System.Composition.Runtime/src/System/Composition/ExportFactoryOfT.cs @@ -17,7 +17,8 @@ public class ExportFactory /// Action invoked upon calls to the Create() method. public ExportFactory(Func> exportCreator) { - _exportLifetimeContextCreator = exportCreator ?? throw new ArgumentNullException(nameof(exportCreator)); + ArgumentNullException.ThrowIfNull(exportCreator); + _exportLifetimeContextCreator = exportCreator; } /// diff --git a/src/libraries/System.Composition.Runtime/src/System/Composition/Hosting/Core/CompositionContract.cs b/src/libraries/System.Composition.Runtime/src/System/Composition/Hosting/Core/CompositionContract.cs index 9dd45fdd66aef9..c13c46f01b0794 100644 --- a/src/libraries/System.Composition.Runtime/src/System/Composition/Hosting/Core/CompositionContract.cs +++ b/src/libraries/System.Composition.Runtime/src/System/Composition/Hosting/Core/CompositionContract.cs @@ -44,7 +44,7 @@ public CompositionContract(Type contractType, string contractName) /// Optionally, a non-empty collection of named constraints that apply to the contract. public CompositionContract(Type contractType, string contractName, IDictionary metadataConstraints) { - if (contractType == null) throw new ArgumentNullException(nameof(contractType)); + ArgumentNullException.ThrowIfNull(contractType); if (metadataConstraints?.Count == 0) throw new ArgumentOutOfRangeException(nameof(metadataConstraints)); _contractType = contractType; @@ -124,7 +124,7 @@ public override string ToString() /// new contract type. public CompositionContract ChangeType(Type newContractType) { - if (newContractType == null) throw new ArgumentNullException(nameof(newContractType)); + ArgumentNullException.ThrowIfNull(newContractType); return new CompositionContract(newContractType, _contractName, _metadataConstraints); } diff --git a/src/libraries/System.Composition.Runtime/src/System/Composition/Runtime/Util/Formatters.cs b/src/libraries/System.Composition.Runtime/src/System/Composition/Runtime/Util/Formatters.cs index 104ba2f4a4cabe..84b4ab92eb2b74 100644 --- a/src/libraries/System.Composition.Runtime/src/System/Composition/Runtime/Util/Formatters.cs +++ b/src/libraries/System.Composition.Runtime/src/System/Composition/Runtime/Util/Formatters.cs @@ -9,15 +9,17 @@ namespace System.Composition.Runtime.Util { internal static class Formatters { - public static string Format(object value) => - value is null ? throw new ArgumentNullException(nameof(value)) : - value is string ? $"\"{value}\"" : - value.ToString(); + public static string Format(object value) + { + ArgumentNullException.ThrowIfNull(value); + return value is string ? $"\"{value}\"" : value.ToString(); + } - public static string Format(Type type) => - type is null ? throw new ArgumentNullException(nameof(type)) : - type.IsConstructedGenericType ? FormatClosedGeneric(type) : - type.Name; + public static string Format(Type type) + { + ArgumentNullException.ThrowIfNull(type); + return type.IsConstructedGenericType ? FormatClosedGeneric(type) : type.Name; + } private static string FormatClosedGeneric(Type closedGenericType) { diff --git a/src/libraries/System.Composition.TypedParts/src/System/Composition/Hosting/ContainerConfiguration.cs b/src/libraries/System.Composition.TypedParts/src/System/Composition/Hosting/ContainerConfiguration.cs index c0fe85ccf824ef..66f2de2b97f4f5 100644 --- a/src/libraries/System.Composition.TypedParts/src/System/Composition/Hosting/ContainerConfiguration.cs +++ b/src/libraries/System.Composition.TypedParts/src/System/Composition/Hosting/ContainerConfiguration.cs @@ -93,7 +93,7 @@ public ContainerConfiguration WithPart(Type partType) /// A configuration object allowing configuration to continue. public ContainerConfiguration WithPart(Type partType, AttributedModelProvider conventions) { - if (partType == null) throw new ArgumentNullException(nameof(partType)); + ArgumentNullException.ThrowIfNull(partType); return WithParts(new[] { partType }, conventions); } diff --git a/src/libraries/System.Composition.TypedParts/src/System/Composition/TypedParts/Util/DirectAttributeContext.cs b/src/libraries/System.Composition.TypedParts/src/System/Composition/TypedParts/Util/DirectAttributeContext.cs index 8dd0b5cdc379b0..5091be40cabe66 100644 --- a/src/libraries/System.Composition.TypedParts/src/System/Composition/TypedParts/Util/DirectAttributeContext.cs +++ b/src/libraries/System.Composition.TypedParts/src/System/Composition/TypedParts/Util/DirectAttributeContext.cs @@ -11,8 +11,8 @@ internal sealed class DirectAttributeContext : AttributedModelProvider { public override IEnumerable GetCustomAttributes(Type reflectedType, Reflection.MemberInfo member) { - if (reflectedType == null) throw new ArgumentNullException(nameof(reflectedType)); - if (member == null) throw new ArgumentNullException(nameof(member)); + ArgumentNullException.ThrowIfNull(reflectedType); + ArgumentNullException.ThrowIfNull(member); if (!(member is TypeInfo) && member.DeclaringType != reflectedType) return Array.Empty(); @@ -24,7 +24,7 @@ public override IEnumerable GetCustomAttributes(Type reflectedType, R { ArgumentNullException.ThrowIfNull(parameter); - if (reflectedType == null) throw new ArgumentNullException(nameof(reflectedType)); + ArgumentNullException.ThrowIfNull(reflectedType); return Attribute.GetCustomAttributes(parameter, false); } diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Activity.cs b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Activity.cs index fd64e889e249d4..13ad55f34acb94 100644 --- a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Activity.cs +++ b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Activity.cs @@ -187,7 +187,11 @@ public Activity SetStatus(ActivityStatusCode code, string? description = null) public string DisplayName { get => _displayName ?? OperationName; - set => _displayName = value ?? throw new ArgumentNullException(nameof(value)); + set + { + ArgumentNullException.ThrowIfNull(value); + _displayName = value; + } } /// Get the ActivitySource object associated with this Activity. diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/ActivitySource.cs b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/ActivitySource.cs index e14fa042526f95..fc41f80dd6b8d7 100644 --- a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/ActivitySource.cs +++ b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/ActivitySource.cs @@ -47,7 +47,8 @@ public ActivitySource(string name, string? version = "", IEnumerable>? tags, object? scope, string? telemetrySchemaUrl) { - Name = name ?? throw new ArgumentNullException(nameof(name)); + ArgumentNullException.ThrowIfNull(name); + Name = name; Version = version; Scope = scope; TelemetrySchemaUrl = telemetrySchemaUrl; diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/ActivitySourceOptions.cs b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/ActivitySourceOptions.cs index d03b393b5dcc9c..f939dd89e301ea 100644 --- a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/ActivitySourceOptions.cs +++ b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/ActivitySourceOptions.cs @@ -18,7 +18,8 @@ public class ActivitySourceOptions /// The name of the object public ActivitySourceOptions(string name) { - _name = name ?? throw new ArgumentNullException(nameof(name)); + ArgumentNullException.ThrowIfNull(name); + _name = name; } /// @@ -27,7 +28,11 @@ public ActivitySourceOptions(string name) public string Name { get => _name; - set => _name = value ?? throw new ArgumentNullException(nameof(value)); + set + { + ArgumentNullException.ThrowIfNull(value); + _name = value; + } } /// diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/ActivityTagsCollection.cs b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/ActivityTagsCollection.cs index 998a66ec7c225a..723c979451577d 100644 --- a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/ActivityTagsCollection.cs +++ b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/ActivityTagsCollection.cs @@ -155,10 +155,7 @@ public void Add(string key, object? value) /// Key and value pair of the tag to add to the collection. public void Add(KeyValuePair item) { - if (item.Key == null) - { - throw new ArgumentNullException(nameof(item)); - } + ArgumentNullException.ThrowIfNull(item.Key, nameof(item)); int index = FindIndex(item.Key); if (index >= 0) diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/DistributedContextPropagator.cs b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/DistributedContextPropagator.cs index 082fc9a42c4fb8..778b21858251f1 100644 --- a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/DistributedContextPropagator.cs +++ b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/DistributedContextPropagator.cs @@ -79,7 +79,8 @@ public static DistributedContextPropagator Current set { - s_current = value ?? throw new ArgumentNullException(nameof(value)); + ArgumentNullException.ThrowIfNull(value); + s_current = value; } } diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/Instrument.cs b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/Instrument.cs index 20c4607f927b1f..b7cd001f20a36e 100644 --- a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/Instrument.cs +++ b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/Instrument.cs @@ -66,8 +66,10 @@ protected Instrument( string? description = default, IEnumerable>? tags = default) { - Meter = meter ?? throw new ArgumentNullException(nameof(meter)); - Name = name ?? throw new ArgumentNullException(nameof(name)); + ArgumentNullException.ThrowIfNull(meter); + Meter = meter; + ArgumentNullException.ThrowIfNull(name); + Name = name; Description = description; Unit = unit; if (tags is not null) diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/Meter.cs b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/Meter.cs index 35c4d953140b3a..a2ffcba1aa4cbf 100644 --- a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/Meter.cs +++ b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/Meter.cs @@ -71,7 +71,8 @@ public Meter(string name, string? version, IEnumerable>? tags, object? scope = null, string? telemetrySchemaUrl = null) { - Name = name ?? throw new ArgumentNullException(nameof(name)); + ArgumentNullException.ThrowIfNull(name); + Name = name; Version = version; if (tags is not null) { diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/MeterOptions.cs b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/MeterOptions.cs index 6e3ce8f43c1c96..f2dd4e924081e7 100644 --- a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/MeterOptions.cs +++ b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/MeterOptions.cs @@ -18,7 +18,11 @@ public class MeterOptions public string Name { get => _name; - set => _name = value ?? throw new ArgumentNullException(nameof(value)); + set + { + ArgumentNullException.ThrowIfNull(value); + _name = value; + } } /// diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/ObservableCounter.cs b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/ObservableCounter.cs index cb7fe046d86bad..5478ebd07a31fb 100644 --- a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/ObservableCounter.cs +++ b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/ObservableCounter.cs @@ -25,7 +25,8 @@ internal ObservableCounter(Meter meter, string name, Func observeValue, strin internal ObservableCounter(Meter meter, string name, Func observeValue, string? unit, string? description, IEnumerable>? tags) : base(meter, name, unit, description, tags) { - _callback = observeValue ?? throw new ArgumentNullException(nameof(observeValue)); + ArgumentNullException.ThrowIfNull(observeValue); + _callback = observeValue; Publish(); } @@ -35,7 +36,8 @@ internal ObservableCounter(Meter meter, string name, Func> observ internal ObservableCounter(Meter meter, string name, Func> observeValue, string? unit, string? description, IEnumerable>? tags) : base(meter, name, unit, description, tags) { - _callback = observeValue ?? throw new ArgumentNullException(nameof(observeValue)); + ArgumentNullException.ThrowIfNull(observeValue); + _callback = observeValue; Publish(); } @@ -45,7 +47,8 @@ internal ObservableCounter(Meter meter, string name, Func>> observeValues, string? unit, string? description, IEnumerable>? tags) : base(meter, name, unit, description, tags) { - _callback = observeValues ?? throw new ArgumentNullException(nameof(observeValues)); + ArgumentNullException.ThrowIfNull(observeValues); + _callback = observeValues; Publish(); } diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/ObservableGauge.cs b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/ObservableGauge.cs index 9071eed9bba329..1781710b46f0ab 100644 --- a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/ObservableGauge.cs +++ b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/ObservableGauge.cs @@ -25,7 +25,8 @@ internal ObservableGauge(Meter meter, string name, Func observeValue, string? internal ObservableGauge(Meter meter, string name, Func observeValue, string? unit, string? description, IEnumerable>? tags) : base(meter, name, unit, description, tags) { - _callback = observeValue ?? throw new ArgumentNullException(nameof(observeValue)); + ArgumentNullException.ThrowIfNull(observeValue); + _callback = observeValue; Publish(); } @@ -35,7 +36,8 @@ internal ObservableGauge(Meter meter, string name, Func> observeV internal ObservableGauge(Meter meter, string name, Func> observeValue, string? unit, string? description, IEnumerable>? tags) : base(meter, name, unit, description, tags) { - _callback = observeValue ?? throw new ArgumentNullException(nameof(observeValue)); + ArgumentNullException.ThrowIfNull(observeValue); + _callback = observeValue; Publish(); } @@ -45,7 +47,8 @@ internal ObservableGauge(Meter meter, string name, Func>> observeValues, string? unit, string? description, IEnumerable>? tags) : base(meter, name, unit, description, tags) { - _callback = observeValues ?? throw new ArgumentNullException(nameof(observeValues)); + ArgumentNullException.ThrowIfNull(observeValues); + _callback = observeValues; Publish(); } diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/ObservableUpDownCounter.cs b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/ObservableUpDownCounter.cs index 948cc02fc7a7a1..f1b710e9b5ed58 100644 --- a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/ObservableUpDownCounter.cs +++ b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/ObservableUpDownCounter.cs @@ -25,7 +25,8 @@ internal ObservableUpDownCounter(Meter meter, string name, Func observeValue, internal ObservableUpDownCounter(Meter meter, string name, Func observeValue, string? unit, string? description, IEnumerable>? tags) : base(meter, name, unit, description, tags) { - _callback = observeValue ?? throw new ArgumentNullException(nameof(observeValue)); + ArgumentNullException.ThrowIfNull(observeValue); + _callback = observeValue; Publish(); } @@ -35,7 +36,8 @@ internal ObservableUpDownCounter(Meter meter, string name, Func> internal ObservableUpDownCounter(Meter meter, string name, Func> observeValue, string? unit, string? description, IEnumerable>? tags) : base(meter, name, unit, description, tags) { - _callback = observeValue ?? throw new ArgumentNullException(nameof(observeValue)); + ArgumentNullException.ThrowIfNull(observeValue); + _callback = observeValue; Publish(); } @@ -45,7 +47,8 @@ internal ObservableUpDownCounter(Meter meter, string name, Func>> observeValues, string? unit, string? description, IEnumerable>? tags) : base(meter, name, unit, description, tags) { - _callback = observeValues ?? throw new ArgumentNullException(nameof(observeValues)); + ArgumentNullException.ThrowIfNull(observeValues); + _callback = observeValues; Publish(); } 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 a30716452c609d..1b9c4736e00d29 100644 --- a/src/libraries/System.Formats.Cbor/src/System.Formats.Cbor.csproj +++ b/src/libraries/System.Formats.Cbor/src/System.Formats.Cbor.csproj @@ -55,6 +55,11 @@ 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 index 95dc9bde882ba4..47d6007ab7c991 100644 --- 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 @@ -1,7 +1,6 @@ // 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; @@ -10,38 +9,14 @@ 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 index d62cd99e4cdbcb..fee5f5da151c44 100644 --- 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 @@ -2,21 +2,14 @@ // 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) @@ -75,20 +68,6 @@ public static byte[] CreateUnsignedBigEndianBytesFromBigInteger(BigInteger value 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) => BinaryPrimitives.ReadUInt16BigEndian(source); @@ -96,48 +75,5 @@ public static ushort ReadHalfBigEndian(ReadOnlySpan source) [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void WriteHalfBigEndian(Span destination, ushort value) => BinaryPrimitives.WriteUInt16BigEndian(destination, value); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float ReadSingleBigEndian(ReadOnlySpan source) - => Int32BitsToSingle(BinaryPrimitives.ReadInt32BigEndian(source)); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void WriteSingleBigEndian(Span destination, float value) - => BinaryPrimitives.WriteInt32BigEndian(destination, SingleToInt32Bits(value)); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static double ReadDoubleBigEndian(ReadOnlySpan source) - => BitConverter.Int64BitsToDouble(BinaryPrimitives.ReadInt64BigEndian(source)); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void WriteDoubleBigEndian(Span destination, double value) - => BinaryPrimitives.WriteInt64BigEndian(destination, BitConverter.DoubleToInt64Bits(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.netstandard.cs index c4d191be9ad54c..d5593d9dc78f02 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.netstandard.cs @@ -53,7 +53,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 +62,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 +119,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 +128,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; 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 c4e77634e5f7ec..069c95e59d06dc 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 @@ -36,7 +36,7 @@ public float ReadSingle() 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; @@ -77,14 +77,14 @@ public double ReadDouble() 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 6a575cc79aa292..2290e1102c1837 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 @@ -397,7 +397,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 62dbb2adfba9df..a8b695a7b46841 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 @@ -127,7 +127,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); 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..e45cc2eca762f1 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 @@ -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 62f45565b291bd..4d1f69ed1e7402 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 @@ -143,7 +143,7 @@ internal static class DecimalHelpers public static void Deconstruct(decimal value, out decimal mantissa, out byte scale) { Span buf = [0, 0, 0, 0]; - CborHelpers.GetBitsFromDecimal(value, buf); + decimal.GetBits(value, buf); int flags = buf[3]; bool isNegative = (flags & SignMask) == SignMask; @@ -155,7 +155,7 @@ public static void Deconstruct(decimal value, out decimal mantissa, out byte sca private static decimal ReconstructFromNegativeScale(decimal mantissa, byte scale) { Span buf = [0, 0, 0, 0]; - CborHelpers.GetBitsFromDecimal(mantissa, buf); + decimal.GetBits(mantissa, buf); int flags = buf[3]; bool isNegative = (flags & SignMask) == SignMask; diff --git a/src/libraries/System.Formats.Cbor/tests/CborTestHelpers.netcoreapp.cs b/src/libraries/System.Formats.Cbor/tests/CborTestHelpers.netcoreapp.cs deleted file mode 100644 index 772adc83f64829..00000000000000 --- a/src/libraries/System.Formats.Cbor/tests/CborTestHelpers.netcoreapp.cs +++ /dev/null @@ -1,13 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -namespace System.Formats.Cbor.Tests -{ - internal static class CborTestHelpers - { - public static readonly DateTimeOffset UnixEpoch = DateTimeOffset.UnixEpoch; - - public static int SingleToInt32Bits(float value) - => BitConverter.SingleToInt32Bits(value); - } -} diff --git a/src/libraries/System.Formats.Cbor/tests/CborTestHelpers.netstandard.cs b/src/libraries/System.Formats.Cbor/tests/CborTestHelpers.netstandard.cs deleted file mode 100644 index 07e595770c3d12..00000000000000 --- a/src/libraries/System.Formats.Cbor/tests/CborTestHelpers.netstandard.cs +++ /dev/null @@ -1,14 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -namespace System.Formats.Cbor.Tests -{ - internal static class CborTestHelpers - { - 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 unsafe int SingleToInt32Bits(float value) - => *((int*)&value); - } -} diff --git a/src/libraries/System.Formats.Cbor/tests/Reader/CborReaderTests.Tag.cs b/src/libraries/System.Formats.Cbor/tests/Reader/CborReaderTests.Tag.cs index c701bb00b1b738..360ddde8462c2e 100644 --- a/src/libraries/System.Formats.Cbor/tests/Reader/CborReaderTests.Tag.cs +++ b/src/libraries/System.Formats.Cbor/tests/Reader/CborReaderTests.Tag.cs @@ -663,7 +663,7 @@ private static (object value, string hexEncoding)[] TaggedValues => (new object[] { CborTag.MimeMessage, 42 }, "d824182a"), (42.0m, "c482201901a4"), ((BigInteger)1, "c24101"), - (CborTestHelpers.UnixEpoch, "c0781c313937302d30312d30315430303a30303a30302e303030303030305a"), + (DateTimeOffset.UnixEpoch, "c0781c313937302d30312d30315430303a30303a30302e303030303030305a"), }; } } diff --git a/src/libraries/System.Formats.Cbor/tests/System.Formats.Cbor.Tests.csproj b/src/libraries/System.Formats.Cbor/tests/System.Formats.Cbor.Tests.csproj index f3239663ae40ac..19630940f68aa6 100644 --- a/src/libraries/System.Formats.Cbor/tests/System.Formats.Cbor.Tests.csproj +++ b/src/libraries/System.Formats.Cbor/tests/System.Formats.Cbor.Tests.csproj @@ -37,7 +37,6 @@ - @@ -45,7 +44,8 @@ - + + diff --git a/src/libraries/System.Formats.Cbor/tests/Writer/CborWriterTests.Simple.cs b/src/libraries/System.Formats.Cbor/tests/Writer/CborWriterTests.Simple.cs index c72ef2e1c077f7..c88c58eb739607 100644 --- a/src/libraries/System.Formats.Cbor/tests/Writer/CborWriterTests.Simple.cs +++ b/src/libraries/System.Formats.Cbor/tests/Writer/CborWriterTests.Simple.cs @@ -54,7 +54,7 @@ public static void WriteSingle_Ctap2Conformance_ShouldPreservePrecision(float in public static void WriteSingle_Ctap2Conformance_ShouldPreservePrecision_NaN() { // float.NaN may differ across architectures, in particular it's negative on x86 and positive elsewhere - byte[] expectedEncoding = ("fa" + CborTestHelpers.SingleToInt32Bits(float.NaN).ToString("x4")).HexToByteArray(); + byte[] expectedEncoding = ("fa" + BitConverter.SingleToInt32Bits(float.NaN).ToString("x4")).HexToByteArray(); var writer = new CborWriter(CborConformanceMode.Ctap2Canonical); writer.WriteSingle(float.NaN); AssertHelper.HexEqual(expectedEncoding, writer.Encode()); diff --git a/src/libraries/System.Formats.Cbor/tests/Writer/CborWriterTests.Tag.cs b/src/libraries/System.Formats.Cbor/tests/Writer/CborWriterTests.Tag.cs index a521f3c1de7ace..5e881543ff4be6 100644 --- a/src/libraries/System.Formats.Cbor/tests/Writer/CborWriterTests.Tag.cs +++ b/src/libraries/System.Formats.Cbor/tests/Writer/CborWriterTests.Tag.cs @@ -232,7 +232,7 @@ from v in TaggedValues new object[] { CborTag.MimeMessage, 42 }, 42.0m, (BigInteger)1, - CborTestHelpers.UnixEpoch, + DateTimeOffset.UnixEpoch, }; } } diff --git a/src/libraries/System.Formats.Nrbf/src/System.Formats.Nrbf.csproj b/src/libraries/System.Formats.Nrbf/src/System.Formats.Nrbf.csproj index a41b98645de865..a103700c24926b 100644 --- a/src/libraries/System.Formats.Nrbf/src/System.Formats.Nrbf.csproj +++ b/src/libraries/System.Formats.Nrbf/src/System.Formats.Nrbf.csproj @@ -18,6 +18,7 @@ Link="System\Diagnostics\CodeAnalysis\RequiresDynamicCodeAttribute.cs" /> + diff --git a/src/libraries/System.Formats.Nrbf/src/System/Formats/Nrbf/ArraySinglePrimitiveRecord.cs b/src/libraries/System.Formats.Nrbf/src/System/Formats/Nrbf/ArraySinglePrimitiveRecord.cs index 7119671137bf00..2955bb9f62af26 100644 --- a/src/libraries/System.Formats.Nrbf/src/System/Formats/Nrbf/ArraySinglePrimitiveRecord.cs +++ b/src/libraries/System.Formats.Nrbf/src/System/Formats/Nrbf/ArraySinglePrimitiveRecord.cs @@ -162,38 +162,17 @@ internal static IReadOnlyList DecodePrimitiveTypes(BinaryReader reader, int c if (typeof(T) == typeof(short) || typeof(T) == typeof(ushort)) { Span span = MemoryMarshal.Cast(result.AsSpan()); -#if NET BinaryPrimitives.ReverseEndianness(span, span); -#else - for (int i = 0; i < span.Length; i++) - { - span[i] = BinaryPrimitives.ReverseEndianness(span[i]); - } -#endif } else if (typeof(T) == typeof(int) || typeof(T) == typeof(uint) || typeof(T) == typeof(float)) { Span span = MemoryMarshal.Cast(result.AsSpan()); -#if NET BinaryPrimitives.ReverseEndianness(span, span); -#else - for (int i = 0; i < span.Length; i++) - { - span[i] = BinaryPrimitives.ReverseEndianness(span[i]); - } -#endif } else if (typeof(T) == typeof(long) || typeof(T) == typeof(ulong) || typeof(T) == typeof(double)) { Span span = MemoryMarshal.Cast(result.AsSpan()); -#if NET BinaryPrimitives.ReverseEndianness(span, span); -#else - for (int i = 0; i < span.Length; i++) - { - span[i] = BinaryPrimitives.ReverseEndianness(span[i]); - } -#endif } } diff --git a/src/libraries/System.IO.Hashing/src/System.IO.Hashing.csproj b/src/libraries/System.IO.Hashing/src/System.IO.Hashing.csproj index 69dc34b2e26ccb..639cf2fa748e18 100644 --- a/src/libraries/System.IO.Hashing/src/System.IO.Hashing.csproj +++ b/src/libraries/System.IO.Hashing/src/System.IO.Hashing.csproj @@ -42,6 +42,7 @@ System.IO.Hashing.XxHash32 + diff --git a/src/libraries/System.IO.Hashing/src/System/IO/Hashing/XxHash128.cs b/src/libraries/System.IO.Hashing/src/System/IO/Hashing/XxHash128.cs index 31c21b251118fb..f87a46488fa46c 100644 --- a/src/libraries/System.IO.Hashing/src/System/IO/Hashing/XxHash128.cs +++ b/src/libraries/System.IO.Hashing/src/System/IO/Hashing/XxHash128.cs @@ -287,7 +287,7 @@ private static Hash128 HashLength4To8(byte* source, uint length, ulong seed) ulong bitflip = (DefaultSecretUInt64_2 ^ DefaultSecretUInt64_3) + seed; ulong keyed = input64 ^ bitflip; - ulong m128High = Multiply64To128(keyed, Prime64_1 + (length << 2), out ulong m128Low); + ulong m128High = Math.BigMul(keyed, Prime64_1 + (length << 2), out ulong m128Low); m128High += (m128Low << 1); m128Low ^= (m128High >> 3); @@ -308,7 +308,7 @@ private static Hash128 HashLength9To16(byte* source, uint length, ulong seed) ulong bitfliph = (DefaultSecretUInt64_6 ^ DefaultSecretUInt64_7) + seed; ulong inputLo = ReadUInt64LE(source); ulong inputHi = ReadUInt64LE(source + length - 8); - ulong m128High = Multiply64To128(inputLo ^ inputHi ^ bitflipl, Prime64_1, out ulong m128Low); + ulong m128High = Math.BigMul(inputLo ^ inputHi ^ bitflipl, Prime64_1, out ulong m128Low); m128Low += (ulong)(length - 1) << 54; inputHi ^= bitfliph; @@ -319,7 +319,7 @@ private static Hash128 HashLength9To16(byte* source, uint length, ulong seed) m128Low ^= BinaryPrimitives.ReverseEndianness(m128High); - ulong h128High = Multiply64To128(m128Low, Prime64_2, out ulong h128Low); + ulong h128High = Math.BigMul(m128Low, Prime64_2, out ulong h128Low); h128High += m128High * (ulong)Prime64_2; h128Low = Avalanche(h128Low); diff --git a/src/libraries/System.IO.Hashing/src/System/IO/Hashing/XxHashShared.cs b/src/libraries/System.IO.Hashing/src/System/IO/Hashing/XxHashShared.cs index d5671725df4c15..ee91a6753bd09c 100644 --- a/src/libraries/System.IO.Hashing/src/System/IO/Hashing/XxHashShared.cs +++ b/src/libraries/System.IO.Hashing/src/System/IO/Hashing/XxHashShared.cs @@ -450,29 +450,11 @@ public static ulong Avalanche(ulong hash) return hash; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ulong Multiply64To128(ulong left, ulong right, out ulong lower) - { -#if NET - return Math.BigMul(left, right, out lower); -#else - ulong lowerLow = Multiply32To64((uint)left, (uint)right); - ulong higherLow = Multiply32To64((uint)(left >> 32), (uint)right); - ulong lowerHigh = Multiply32To64((uint)left, (uint)(right >> 32)); - ulong higherHigh = Multiply32To64((uint)(left >> 32), (uint)(right >> 32)); - - ulong cross = (lowerLow >> 32) + (higherLow & 0xFFFFFFFF) + lowerHigh; - ulong upper = (higherLow >> 32) + (cross >> 32) + higherHigh; - lower = (cross << 32) | (lowerLow & 0xFFFFFFFF); - return upper; -#endif - } - /// Calculates a 64-bit to 128-bit multiply, then XOR folds it. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ulong Multiply64To128ThenFold(ulong left, ulong right) { - ulong upper = Multiply64To128(left, right, out ulong lower); + ulong upper = Math.BigMul(left, right, out ulong lower); return lower ^ upper; } diff --git a/src/libraries/System.IO.Packaging/src/System/IO/Packaging/IgnoreFlushAndCloseStream.cs b/src/libraries/System.IO.Packaging/src/System/IO/Packaging/IgnoreFlushAndCloseStream.cs index 72e6076b68a96d..39bf0ed074d583 100644 --- a/src/libraries/System.IO.Packaging/src/System/IO/Packaging/IgnoreFlushAndCloseStream.cs +++ b/src/libraries/System.IO.Packaging/src/System/IO/Packaging/IgnoreFlushAndCloseStream.cs @@ -19,7 +19,8 @@ internal sealed class IgnoreFlushAndCloseStream : Stream /// internal IgnoreFlushAndCloseStream(Stream stream) { - _stream = stream ?? throw new ArgumentNullException(nameof(stream)); + ArgumentNullException.ThrowIfNull(stream); + _stream = stream; } #endregion Constructor diff --git a/src/libraries/System.IO.Packaging/src/System/IO/Packaging/InterleavedZipPackagePartStream.cs b/src/libraries/System.IO.Packaging/src/System/IO/Packaging/InterleavedZipPackagePartStream.cs index 82ec1f6a71200b..143e5e4b3f84b3 100644 --- a/src/libraries/System.IO.Packaging/src/System/IO/Packaging/InterleavedZipPackagePartStream.cs +++ b/src/libraries/System.IO.Packaging/src/System/IO/Packaging/InterleavedZipPackagePartStream.cs @@ -231,14 +231,7 @@ public override void SetLength(long newLength) CheckClosed(); // Check argument and stream capabilities. -#if NET ArgumentOutOfRangeException.ThrowIfNegative(newLength); -#else - if (newLength < 0) - { - throw new ArgumentOutOfRangeException(nameof(newLength)); - } -#endif if (!CanWrite) { throw new NotSupportedException(SR.StreamDoesNotSupportWrite); diff --git a/src/libraries/System.IO.Packaging/src/System/IO/Packaging/PackUriHelper.PackUriScheme.cs b/src/libraries/System.IO.Packaging/src/System/IO/Packaging/PackUriHelper.PackUriScheme.cs index 692efce1cf81c7..59956b6cc9ab6d 100644 --- a/src/libraries/System.IO.Packaging/src/System/IO/Packaging/PackUriHelper.PackUriScheme.cs +++ b/src/libraries/System.IO.Packaging/src/System/IO/Packaging/PackUriHelper.PackUriScheme.cs @@ -248,8 +248,7 @@ private static void EnsurePackSchemeRegistered() /// private static Uri ValidatePackageUri(Uri packageUri) { - if (packageUri == null) - throw new ArgumentNullException(nameof(packageUri)); + ArgumentNullException.ThrowIfNull(packageUri); if (!packageUri.IsAbsoluteUri) throw new ArgumentException(SR.UriShouldBeAbsolute, nameof(packageUri)); @@ -260,8 +259,7 @@ private static Uri ValidatePackageUri(Uri packageUri) //validates is a given uri has pack:// scheme private static Uri ValidatePackUri(Uri packUri) { - if (packUri == null) - throw new ArgumentNullException(nameof(packUri)); + ArgumentNullException.ThrowIfNull(packUri); if (!packUri.IsAbsoluteUri) throw new ArgumentException(SR.UriShouldBeAbsolute, nameof(packUri)); diff --git a/src/libraries/System.IO.Packaging/src/System/IO/Packaging/Package.cs b/src/libraries/System.IO.Packaging/src/System/IO/Packaging/Package.cs index 2a6adbac5a4ab4..da810f406fe60b 100644 --- a/src/libraries/System.IO.Packaging/src/System/IO/Packaging/Package.cs +++ b/src/libraries/System.IO.Packaging/src/System/IO/Packaging/Package.cs @@ -214,11 +214,9 @@ public PackagePart CreatePart(Uri partUri, ThrowIfObjectDisposed(); ThrowIfReadOnly(); - if (partUri == null) - throw new ArgumentNullException(nameof(partUri)); + ArgumentNullException.ThrowIfNull(partUri); - if (contentType == null) - throw new ArgumentNullException(nameof(contentType)); + ArgumentNullException.ThrowIfNull(contentType); ThrowIfCompressionOptionInvalid(compressionOption); @@ -283,8 +281,7 @@ public virtual bool PartExists(Uri partUri) { ThrowIfObjectDisposed(); - if (partUri == null) - throw new ArgumentNullException(nameof(partUri)); + ArgumentNullException.ThrowIfNull(partUri); PackUriHelper.ValidatedPartUri validatePartUri = PackUriHelper.ValidatePartUri(partUri); @@ -309,8 +306,7 @@ public void DeletePart(Uri partUri) ThrowIfObjectDisposed(); ThrowIfReadOnly(); - if (partUri == null) - throw new ArgumentNullException(nameof(partUri)); + ArgumentNullException.ThrowIfNull(partUri); PackUriHelper.ValidatedPartUri validatedPartUri = (PackUriHelper.ValidatedPartUri)PackUriHelper.ValidatePartUri(partUri); @@ -623,8 +619,7 @@ public void DeleteRelationship(string id) ThrowIfObjectDisposed(); ThrowIfReadOnly(); - if (id == null) - throw new ArgumentNullException(nameof(id)); + ArgumentNullException.ThrowIfNull(id); InternalRelationshipCollection.ThrowIfInvalidXsdId(id); @@ -664,8 +659,7 @@ public PackageRelationshipCollection GetRelationshipsByType(string relationshipT ThrowIfObjectDisposed(); ThrowIfWriteOnly(); - if (relationshipType == null) - throw new ArgumentNullException(nameof(relationshipType)); + ArgumentNullException.ThrowIfNull(relationshipType); InternalRelationshipCollection.ThrowIfInvalidRelationshipType(relationshipType); @@ -1136,8 +1130,7 @@ private bool DoCloseRelationshipsXml(PackagePart p) ThrowIfObjectDisposed(); ThrowIfWriteOnly(); - if (partUri == null) - throw new ArgumentNullException(nameof(partUri)); + ArgumentNullException.ThrowIfNull(partUri); PackUriHelper.ValidatedPartUri validatePartUri = PackUriHelper.ValidatePartUri(partUri); @@ -1175,8 +1168,7 @@ private bool DoCloseRelationshipsXml(PackagePart p) ThrowIfObjectDisposed(); ThrowIfWriteOnly(); - if (id == null) - throw new ArgumentNullException(nameof(id)); + ArgumentNullException.ThrowIfNull(id); InternalRelationshipCollection.ThrowIfInvalidXsdId(id); diff --git a/src/libraries/System.IO.Packaging/src/System/IO/Packaging/PackagePart.cs b/src/libraries/System.IO.Packaging/src/System/IO/Packaging/PackagePart.cs index 3d741448c0c65c..46aba7ee243620 100644 --- a/src/libraries/System.IO.Packaging/src/System/IO/Packaging/PackagePart.cs +++ b/src/libraries/System.IO.Packaging/src/System/IO/Packaging/PackagePart.cs @@ -424,8 +424,7 @@ public void DeleteRelationship(string id) CheckInvalidState(); _container.ThrowIfReadOnly(); - if (id == null) - throw new ArgumentNullException(nameof(id)); + ArgumentNullException.ThrowIfNull(id); InternalRelationshipCollection.ThrowIfInvalidXsdId(id); @@ -468,8 +467,7 @@ public PackageRelationshipCollection GetRelationshipsByType(string relationshipT CheckInvalidState(); _container.ThrowIfWriteOnly(); - if (relationshipType == null) - throw new ArgumentNullException(nameof(relationshipType)); + ArgumentNullException.ThrowIfNull(relationshipType); InternalRelationshipCollection.ThrowIfInvalidRelationshipType(relationshipType); @@ -739,8 +737,7 @@ private void ThrowIfRelationship() CheckInvalidState(); _container.ThrowIfWriteOnly(); - if (id == null) - throw new ArgumentNullException(nameof(id)); + ArgumentNullException.ThrowIfNull(id); InternalRelationshipCollection.ThrowIfInvalidXsdId(id); diff --git a/src/libraries/System.IO.Packaging/src/System/IO/Packaging/ZipPackage.cs b/src/libraries/System.IO.Packaging/src/System/IO/Packaging/ZipPackage.cs index 9786c4653851b2..aedc8c7535d18f 100644 --- a/src/libraries/System.IO.Packaging/src/System/IO/Packaging/ZipPackage.cs +++ b/src/libraries/System.IO.Packaging/src/System/IO/Packaging/ZipPackage.cs @@ -40,8 +40,7 @@ protected override PackagePart CreatePartCore(Uri partUri, //Validating the PartUri - this method will do the argument checking required for uri. partUri = PackUriHelper.ValidatePartUri(partUri); - if (contentType == null) - throw new ArgumentNullException(nameof(contentType)); + ArgumentNullException.ThrowIfNull(contentType); Package.ThrowIfCompressionOptionInvalid(compressionOption); diff --git a/src/libraries/System.Net.ServerSentEvents/src/System/Net/ServerSentEvents/SseParser_1.cs b/src/libraries/System.Net.ServerSentEvents/src/System/Net/ServerSentEvents/SseParser_1.cs index f9cafd6d6f2b68..52dbe9ffdc1d55 100644 --- a/src/libraries/System.Net.ServerSentEvents/src/System/Net/ServerSentEvents/SseParser_1.cs +++ b/src/libraries/System.Net.ServerSentEvents/src/System/Net/ServerSentEvents/SseParser_1.cs @@ -93,11 +93,7 @@ internal SseParser(Stream stream, SseParserOptions options) _itemParser = options.ItemParser; _maxBufferSize = options.MaxBufferSize == -1 ? DefaultMaxBufferSize : Math.Max(options.MaxBufferSize, DefaultArrayPoolRentSize); -#if NET _maxBufferSize = Math.Min(_maxBufferSize, Array.MaxLength); -#else - _maxBufferSize = Math.Min(_maxBufferSize, 0x7FFFFFC7); -#endif } /// Gets an enumerable of the server-sent events from this parser. diff --git a/src/libraries/System.Reflection.Metadata/src/System.Reflection.Metadata.csproj b/src/libraries/System.Reflection.Metadata/src/System.Reflection.Metadata.csproj index 8884ef677750d4..b8bf986771753d 100644 --- a/src/libraries/System.Reflection.Metadata/src/System.Reflection.Metadata.csproj +++ b/src/libraries/System.Reflection.Metadata/src/System.Reflection.Metadata.csproj @@ -96,6 +96,8 @@ The System.Reflection.Metadata library is built-in as part of the shared framewo + + diff --git a/src/libraries/System.Reflection.Metadata/src/System/Reflection/Metadata/BlobWriterImpl.cs b/src/libraries/System.Reflection.Metadata/src/System/Reflection/Metadata/BlobWriterImpl.cs index fc27827c788b6b..db33cf204bdaae 100644 --- a/src/libraries/System.Reflection.Metadata/src/System/Reflection/Metadata/BlobWriterImpl.cs +++ b/src/libraries/System.Reflection.Metadata/src/System/Reflection/Metadata/BlobWriterImpl.cs @@ -189,15 +189,7 @@ internal static int WriteScalarConstant(Span bytes, object? value) } else if (type == typeof(double)) { -#if NET BinaryPrimitives.WriteDoubleLittleEndian(bytes, (double)value); -#else - double v = (double)value; - unsafe - { - BinaryPrimitives.WriteUInt64LittleEndian(bytes, *(ulong*)(&v)); - } -#endif return sizeof(double); } else if (type == typeof(short)) @@ -217,15 +209,7 @@ internal static int WriteScalarConstant(Span bytes, object? value) } else if (type == typeof(float)) { -#if NET BinaryPrimitives.WriteSingleLittleEndian(bytes, (float)value); -#else - float v = (float)value; - unsafe - { - BinaryPrimitives.WriteUInt32LittleEndian(bytes, *(uint*)(&v)); - } -#endif return sizeof(float); } else if (type == typeof(ushort)) diff --git a/src/libraries/System.Text.Encodings.Web/src/System.Text.Encodings.Web.csproj b/src/libraries/System.Text.Encodings.Web/src/System.Text.Encodings.Web.csproj index c52e032682abe5..d5760ad0d6b5b8 100644 --- a/src/libraries/System.Text.Encodings.Web/src/System.Text.Encodings.Web.csproj +++ b/src/libraries/System.Text.Encodings.Web/src/System.Text.Encodings.Web.csproj @@ -54,6 +54,7 @@ System.Text.Encodings.Web.JavaScriptEncoder + diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/JsonNode/JsonArrayTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/JsonNode/JsonArrayTests.cs index 8b89286b39237f..368e05c478dd39 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/JsonNode/JsonArrayTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/JsonNode/JsonArrayTests.cs @@ -31,32 +31,6 @@ void Verify(JsonArray jsonArray, JsonNodeOptions? expectedOptions) } } - [Theory] - [InlineData(false)] - [InlineData(true)] - public static void SpanConstructor_CopiesSliceAndAssignsParents(bool withOptions) - { - JsonNode?[] items = [-1, 1, null, 3, -2]; - ReadOnlySpan span = items.AsSpan(1, 3); - JsonNodeOptions options = new() { PropertyNameCaseInsensitive = true }; - - JsonArray array = withOptions ? new JsonArray(options, span) : new JsonArray(span); - - Assert.Equal(3, array.Count); - Assert.Equal("[1,null,3]", array.ToJsonString()); - Assert.Equal(withOptions ? options : (JsonNodeOptions?)null, array.Options); - Assert.Same(items[1], array[0]); - Assert.Null(array[1]); - Assert.Same(items[3], array[2]); - Assert.Same(array, array[0]?.Parent); - Assert.Same(array, array[2]?.Parent); - Assert.Null(items[0]?.Parent); - Assert.Null(items[4]?.Parent); - - items[1] = 4; - Assert.Equal("[1,null,3]", array.ToJsonString()); - } - [Fact] public static void FromElement() { diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/JsonNode/JsonObjectTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/JsonNode/JsonObjectTests.cs index 0014b4c9efb039..f9d01d456c4d9c 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/JsonNode/JsonObjectTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/JsonNode/JsonObjectTests.cs @@ -1656,22 +1656,6 @@ public static void TryAdd_ThrowsArgumentNullException() Assert.Throws(() => { jObject.TryAdd(null, 33); }); } - [Theory] - [InlineData(false)] - [InlineData(true)] - public static void TryAdd_NullPropertyNamePreservesState(bool reportIndex) - { - JsonObject jsonObject = new() { ["existing"] = 1 }; - JsonNode value = JsonValue.Create(2); - Action add = reportIndex - ? () => jsonObject.TryAdd(propertyName: null, value, out _) - : () => jsonObject.TryAdd(propertyName: null, value); - - Assert.Throws("propertyName", add); - Assert.Equal("""{"existing":1}""", jsonObject.ToJsonString()); - Assert.Null(value.Parent); - } - [Fact] public static void TryGetPropertyValue_ThrowsArgumentNullException() { diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/JsonWriterOptionsTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/JsonWriterOptionsTests.cs index 09ae951d57bc44..5c742d6294b689 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/JsonWriterOptionsTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/JsonWriterOptionsTests.cs @@ -193,14 +193,5 @@ public static void JsonWriterOptions_NewLine_Null_Throws() var options = new JsonWriterOptions(); Assert.Throws(() => options.NewLine = null); } - - [Fact] - public static void JsonWriterOptions_NewLine_NullPreservesValue() - { - JsonWriterOptions options = new() { NewLine = "\r\n" }; - - Assert.Throws("value", () => options.NewLine = null); - Assert.Equal("\r\n", options.NewLine); - } } } diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/EnumConverterTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/EnumConverterTests.cs index 452e546de5d3cf..3db757e8f23ed7 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/EnumConverterTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/EnumConverterTests.cs @@ -1413,36 +1413,6 @@ public static void EnumWithOverlappingBitsTests() Assert.Equal("\"BITS01, BIT3\"", json2); } - [Theory] - [InlineData(EnumWithHighBits.None, "\"None\"")] - [InlineData(EnumWithHighBits.Low, "\"Low\"")] - [InlineData(EnumWithHighBits.High, "\"High\"")] - [InlineData(EnumWithHighBits.HighAndLow, "\"HighAndLow\"")] - [InlineData(EnumWithHighBits.High | EnumWithHighBits.Middle, "\"Middle, High\"")] - [InlineData(EnumWithHighBits.All, "\"All\"")] - public static void EnumWithHighBits_Roundtrips(EnumWithHighBits value, string expectedJson) - { - JsonSerializerOptions options = new() { Converters = { new JsonStringEnumConverter() } }; - - string json = JsonSerializer.Serialize(value, options); - - Assert.Equal(expectedJson, json); - Assert.Equal(value, JsonSerializer.Deserialize(json, options)); - } - - [Flags] - public enum EnumWithHighBits : ulong - { - None = 0, - Low = 1, - Middle = 2, - Pair = Low | Middle, - High = 1UL << 63, - HighAndLow = High | Low, - HighAndPair = High | Pair, - All = ulong.MaxValue, - } - [Flags] public enum EnumWithOverlappingBits { diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/JsonElementTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/JsonElementTests.cs index a8d4c0b42c0ff1..30272cc7acb208 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/JsonElementTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/JsonElementTests.cs @@ -3,7 +3,6 @@ using System.Buffers; using System.Collections.Generic; -using System.Globalization; using System.IO; using System.Threading; using System.Threading.Tasks; @@ -139,31 +138,6 @@ public static void DeepEquals_EqualValuesReturnTrue(string value1, string value2 Assert.True(JsonElement.DeepEquals(element2, element1)); } - [Theory] - [InlineData(0)] - [InlineData(1)] - [InlineData(15)] - [InlineData(16)] - [InlineData(31)] - [InlineData(32)] - [InlineData(255)] - [InlineData(256)] - public static void DeepEquals_NumberNormalizationBoundaries(int zeroCount) - { - string zeros = new('0', zeroCount); - JsonElement fraction = JsonElement.Parse("0." + zeros + "12345" + zeros); - JsonElement fractionExponent = JsonElement.Parse("12345e-" + (zeroCount + 5).ToString(CultureInfo.InvariantCulture)); - JsonElement differentFraction = JsonElement.Parse("0." + zeros + "12346" + zeros); - JsonElement integral = JsonElement.Parse("12345" + zeros); - JsonElement integralExponent = JsonElement.Parse("12345e" + zeroCount.ToString(CultureInfo.InvariantCulture)); - JsonElement zero = JsonElement.Parse("-0." + zeros + "0"); - - Assert.True(JsonElement.DeepEquals(fraction, fractionExponent)); - Assert.True(JsonElement.DeepEquals(integral, integralExponent)); - Assert.True(JsonElement.DeepEquals(zero, JsonElement.Parse("0"))); - Assert.False(JsonElement.DeepEquals(fraction, differentFraction)); - } - [Theory] // Kind mismatch [InlineData("null", "false")] diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/NamingPolicyUnitTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/NamingPolicyUnitTests.cs index 3cd5e646f83656..91ae27fe40d2f2 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/NamingPolicyUnitTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/NamingPolicyUnitTests.cs @@ -64,21 +64,6 @@ public static void CamelCaseNullNameReturnsNull() Assert.Null(policy.ConvertName(null)); } - [Theory] - [InlineData(1)] - [InlineData(2)] - [InlineData(31)] - [InlineData(256)] - [InlineData(1024)] - public static void CamelCase_CopiesEntireName(int prefixLength) - { - const string Suffix = "Value\0\ud800"; - string name = new string('A', prefixLength) + Suffix; - string expected = new string('a', prefixLength) + Suffix; - - Assert.Equal(expected, JsonNamingPolicy.CamelCase.ConvertName(name)); - } - [Theory, OuterLoop] [MemberData(nameof(GetValidMemberNames))] public static void CamelCaseNamingPolicyMatchesNewtonsoftNamingStrategy(string name) diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/PropertyNameTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/PropertyNameTests.cs index 03312914f28162..fc621891302f2d 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/PropertyNameTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/PropertyNameTests.cs @@ -101,20 +101,5 @@ public async Task JsonNamingPolicyAttribute_CustomDerived_TypeLevel_OverridesGlo string json = await Serializer.SerializeWrapper(new ClassWithCustomDerivedNamingPolicyAttribute { MyValue = "test" }, options); Assert.Equal("""{"MYVALUE":"test"}""", json); } - - [Fact] - public void JsonNamingPolicyAttribute_CustomPolicy_ValidatesArgument() - { - Assert.Throws("namingPolicy", () => new CustomNamingPolicyAttribute(null)); - - JsonNamingPolicy namingPolicy = JsonNamingPolicy.CamelCase; - var attribute = new CustomNamingPolicyAttribute(namingPolicy); - Assert.Same(namingPolicy, attribute.NamingPolicy); - } - - private sealed class CustomNamingPolicyAttribute : JsonNamingPolicyAttribute - { - public CustomNamingPolicyAttribute(JsonNamingPolicy namingPolicy) : base(namingPolicy) { } - } } } diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Utf8JsonReaderTests.MultiSegment.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Utf8JsonReaderTests.MultiSegment.cs index 43389785d987a9..d58ae54e66c6a7 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Utf8JsonReaderTests.MultiSegment.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Utf8JsonReaderTests.MultiSegment.cs @@ -37,39 +37,6 @@ public static void InitialStateMultiSegment() Assert.False(json.Read()); } - [Theory] - [InlineData(false, false)] - [InlineData(false, true)] - [InlineData(true, false)] - [InlineData(true, true)] - public static void ReadSequence_WithEmptyBoundarySegments(bool prependEmpty, bool appendEmpty) - { - ReadOnlyMemory data = """{"value":"\u0041"}"""u8.ToArray(); - var firstSegment = new BufferSegment(prependEmpty ? ReadOnlyMemory.Empty : data); - BufferSegment lastSegment = prependEmpty ? firstSegment.Append(data) : firstSegment; - - if (appendEmpty) - { - lastSegment = lastSegment.Append(ReadOnlyMemory.Empty); - } - - var sequence = new ReadOnlySequence(firstSegment, 0, lastSegment, lastSegment.Memory.Length); - var reader = new Utf8JsonReader(sequence); - - Assert.True(reader.Read()); - Assert.Equal(JsonTokenType.StartObject, reader.TokenType); - Assert.True(reader.Read()); - Assert.Equal(JsonTokenType.PropertyName, reader.TokenType); - Assert.True(reader.ValueTextEquals("value"u8)); - Assert.True(reader.Read()); - Assert.Equal(JsonTokenType.String, reader.TokenType); - Assert.True(reader.ValueTextEquals("A"u8)); - Assert.True(reader.Read()); - Assert.Equal(JsonTokenType.EndObject, reader.TokenType); - Assert.False(reader.Read()); - Assert.Equal(data.Length, reader.BytesConsumed); - } - [Fact] public static void EmptyJsonMultiSegmentIsInvalid() { diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Utf8JsonWriterTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Utf8JsonWriterTests.cs index d86bbdbdd74677..e0828b3e97784b 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Utf8JsonWriterTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Utf8JsonWriterTests.cs @@ -1200,28 +1200,6 @@ public void InvalidReset(JsonWriterOptions options) Assert.Throws(() => writeToIBW.Reset(stream, options)); } - [Theory] - [InlineData(false, false)] - [InlineData(false, true)] - [InlineData(true, false)] - [InlineData(true, true)] - public static void Reset_NullOutputPreservesValidationOrder(bool useStream, bool withOptions) - { - using var writer = new Utf8JsonWriter(new FixedSizedBufferWriter(256)); - Action reset = (useStream, withOptions) switch - { - (true, true) => () => writer.Reset((Stream)null, default), - (true, false) => () => writer.Reset((Stream)null), - (false, true) => () => writer.Reset((IBufferWriter)null, default), - (false, false) => () => writer.Reset((IBufferWriter)null), - }; - - Assert.Throws(useStream ? "utf8Json" : "bufferWriter", reset); - - writer.Dispose(); - Assert.Throws(reset); - } - [Fact] public static void JsonSerializerCacheNotPoisonedByDisposedWriter() { diff --git a/src/libraries/System.Text.RegularExpressions/gen/System.Text.RegularExpressions.Generator.csproj b/src/libraries/System.Text.RegularExpressions/gen/System.Text.RegularExpressions.Generator.csproj index 642d6d750443fc..3b051a524b5493 100644 --- a/src/libraries/System.Text.RegularExpressions/gen/System.Text.RegularExpressions.Generator.csproj +++ b/src/libraries/System.Text.RegularExpressions/gen/System.Text.RegularExpressions.Generator.csproj @@ -21,6 +21,10 @@ + + + + diff --git a/src/libraries/System.Threading.Tasks.Dataflow/src/Base/DataflowBlock.cs b/src/libraries/System.Threading.Tasks.Dataflow/src/Base/DataflowBlock.cs index 066176972a3457..d7a10b915c2d13 100644 --- a/src/libraries/System.Threading.Tasks.Dataflow/src/Base/DataflowBlock.cs +++ b/src/libraries/System.Threading.Tasks.Dataflow/src/Base/DataflowBlock.cs @@ -133,7 +133,7 @@ DataflowMessageStatus ITargetBlock.OfferMessage(DataflowMessageHeader message // Validate arguments. Some targets may have a null source, but FilteredLinkPropagator // is an internal target that should only ever have source non-null. if (!messageHeader.IsValid) throw new ArgumentException(SR.Argument_InvalidMessageHeader, nameof(messageHeader)); - if (source == null) throw new ArgumentNullException(nameof(source)); + ArgumentNullException.ThrowIfNull(source); // Run the filter. bool passedFilter = RunPredicate(messageValue); @@ -619,7 +619,7 @@ internal void OfferToTarget() { // Validate arguments if (!messageHeader.IsValid) throw new ArgumentException(SR.Argument_InvalidMessageHeader, nameof(messageHeader)); - if (target == null) throw new ArgumentNullException(nameof(target)); + ArgumentNullException.ThrowIfNull(target); // If the task has already completed, there's nothing to consume. This could happen if // cancellation was already requested and completed the task as a result. @@ -664,7 +664,7 @@ bool ISourceBlock.ReserveMessage(DataflowMessageHeader messageHeader, I { // Validate arguments if (!messageHeader.IsValid) throw new ArgumentException(SR.Argument_InvalidMessageHeader, nameof(messageHeader)); - if (target == null) throw new ArgumentNullException(nameof(target)); + ArgumentNullException.ThrowIfNull(target); // If the task has already completed, such as due to cancellation, there's nothing to reserve. if (Task.IsCompleted) return false; @@ -681,7 +681,7 @@ void ISourceBlock.ReleaseReservation(DataflowMessageHeader messageHeade { // Validate arguments if (!messageHeader.IsValid) throw new ArgumentException(SR.Argument_InvalidMessageHeader, nameof(messageHeader)); - if (target == null) throw new ArgumentNullException(nameof(target)); + ArgumentNullException.ThrowIfNull(target); // If this is not the message we posted, bail if (messageHeader.Id != Common.SINGLE_MESSAGE_ID) @@ -1401,8 +1401,9 @@ public static Task OutputAvailableAsync(this ISourceBlock OutputAvailableAsync( this ISourceBlock source, CancellationToken cancellationToken) { + ArgumentNullException.ThrowIfNull(source); + return - source is null ? throw new ArgumentNullException(nameof(source)) : cancellationToken.IsCancellationRequested ? Common.CreateTaskFromCancellation(cancellationToken) : Impl(source, cancellationToken); @@ -1460,7 +1461,7 @@ public OutputAvailableAsyncTarget() : DataflowMessageStatus ITargetBlock.OfferMessage(DataflowMessageHeader messageHeader, T messageValue, ISourceBlock? source, bool consumeToAccept) { if (!messageHeader.IsValid) throw new ArgumentException(SR.Argument_InvalidMessageHeader, nameof(messageHeader)); - if (source == null) throw new ArgumentNullException(nameof(source)); + ArgumentNullException.ThrowIfNull(source); TrySetResult(true); diff --git a/src/libraries/System.Threading.Tasks.Dataflow/src/Base/DataflowBlockOptions.cs b/src/libraries/System.Threading.Tasks.Dataflow/src/Base/DataflowBlockOptions.cs index 185a5183300ce9..4568ad04d5f051 100644 --- a/src/libraries/System.Threading.Tasks.Dataflow/src/Base/DataflowBlockOptions.cs +++ b/src/libraries/System.Threading.Tasks.Dataflow/src/Base/DataflowBlockOptions.cs @@ -111,7 +111,7 @@ public TaskScheduler TaskScheduler set { Debug.Assert(this != Default, "Default instance is supposed to be immutable."); - if (value == null) throw new ArgumentNullException(nameof(value)); + ArgumentNullException.ThrowIfNull(value); _taskScheduler = value; } } @@ -173,7 +173,7 @@ public string NameFormat set { Debug.Assert(this != Default, "Default instance is supposed to be immutable."); - if (value == null) throw new ArgumentNullException(nameof(value)); + ArgumentNullException.ThrowIfNull(value); _nameFormat = value; } } diff --git a/src/libraries/System.Threading.Tasks.Dataflow/src/Blocks/BatchBlock.cs b/src/libraries/System.Threading.Tasks.Dataflow/src/Blocks/BatchBlock.cs index db265298ffbbb4..5f33c63d18298c 100644 --- a/src/libraries/System.Threading.Tasks.Dataflow/src/Blocks/BatchBlock.cs +++ b/src/libraries/System.Threading.Tasks.Dataflow/src/Blocks/BatchBlock.cs @@ -47,7 +47,7 @@ public BatchBlock(int batchSize, GroupingDataflowBlockOptions dataflowBlockOptio { // Validate arguments if (batchSize < 1) throw new ArgumentOutOfRangeException(nameof(batchSize), SR.ArgumentOutOfRange_GenericPositive); - if (dataflowBlockOptions == null) throw new ArgumentNullException(nameof(dataflowBlockOptions)); + ArgumentNullException.ThrowIfNull(dataflowBlockOptions); if (dataflowBlockOptions.BoundedCapacity > 0 && dataflowBlockOptions.BoundedCapacity < batchSize) throw new ArgumentOutOfRangeException(nameof(batchSize), SR.ArgumentOutOfRange_BatchSizeMustBeNoGreaterThanBoundedCapacity); // Ensure we have options that can't be changed by the caller diff --git a/src/libraries/System.Threading.Tasks.Dataflow/src/Blocks/BatchedJoinBlock.cs b/src/libraries/System.Threading.Tasks.Dataflow/src/Blocks/BatchedJoinBlock.cs index 559edd480d94d5..9e43411867061b 100644 --- a/src/libraries/System.Threading.Tasks.Dataflow/src/Blocks/BatchedJoinBlock.cs +++ b/src/libraries/System.Threading.Tasks.Dataflow/src/Blocks/BatchedJoinBlock.cs @@ -55,7 +55,7 @@ public BatchedJoinBlock(int batchSize, GroupingDataflowBlockOptions dataflowBloc { // Validate arguments if (batchSize < 1) throw new ArgumentOutOfRangeException(nameof(batchSize), SR.ArgumentOutOfRange_GenericPositive); - if (dataflowBlockOptions == null) throw new ArgumentNullException(nameof(dataflowBlockOptions)); + ArgumentNullException.ThrowIfNull(dataflowBlockOptions); if (!dataflowBlockOptions.Greedy) throw new ArgumentException(SR.Argument_NonGreedyNotSupported, nameof(dataflowBlockOptions)); if (dataflowBlockOptions.BoundedCapacity != DataflowBlockOptions.Unbounded) throw new ArgumentException(SR.Argument_BoundedCapacityNotSupported, nameof(dataflowBlockOptions)); @@ -300,7 +300,7 @@ public BatchedJoinBlock(int batchSize, GroupingDataflowBlockOptions dataflowBloc { // Validate arguments if (batchSize < 1) throw new ArgumentOutOfRangeException(nameof(batchSize), SR.ArgumentOutOfRange_GenericPositive); - if (dataflowBlockOptions == null) throw new ArgumentNullException(nameof(dataflowBlockOptions)); + ArgumentNullException.ThrowIfNull(dataflowBlockOptions); if (!dataflowBlockOptions.Greedy || dataflowBlockOptions.BoundedCapacity != DataflowBlockOptions.Unbounded) { diff --git a/src/libraries/System.Threading.Tasks.Dataflow/src/Blocks/BroadcastBlock.cs b/src/libraries/System.Threading.Tasks.Dataflow/src/Blocks/BroadcastBlock.cs index a900995c99cfba..985112c69a7b7a 100644 --- a/src/libraries/System.Threading.Tasks.Dataflow/src/Blocks/BroadcastBlock.cs +++ b/src/libraries/System.Threading.Tasks.Dataflow/src/Blocks/BroadcastBlock.cs @@ -1033,7 +1033,7 @@ internal IDisposable LinkTo(ITargetBlock target, DataflowLinkOptions li { // Validate arguments if (!messageHeader.IsValid) throw new ArgumentException(SR.Argument_InvalidMessageHeader, nameof(messageHeader)); - if (target == null) throw new ArgumentNullException(nameof(target)); + ArgumentNullException.ThrowIfNull(target); TOutput? valueToClone; lock (OutgoingLock) // We may currently be calling out under this lock to the target; requires it to be reentrant @@ -1073,7 +1073,7 @@ internal bool ReserveMessage(DataflowMessageHeader messageHeader, ITargetBlock? transformSync, Func> transform) : /// The is null (Nothing in Visual Basic). public TransformManyBlock(Func> transform, ExecutionDataflowBlockOptions dataflowBlockOptions) { - if (transform == null) throw new ArgumentNullException(nameof(transform)); + ArgumentNullException.ThrowIfNull(transform); Initialize(messageWithId => ProcessMessage(transform, messageWithId), dataflowBlockOptions, ref _source, ref _target, ref _reorderingBuffer, TargetCoreOptions.None); } @@ -91,7 +91,7 @@ public TransformManyBlock(Func>> transform) : /// The is null (Nothing in Visual Basic). public TransformManyBlock(Func>> transform, ExecutionDataflowBlockOptions dataflowBlockOptions) { - if (transform == null) throw new ArgumentNullException(nameof(transform)); + ArgumentNullException.ThrowIfNull(transform); Initialize(messageWithId => ProcessMessageWithTask(transform, messageWithId), dataflowBlockOptions, ref _source, ref _target, ref _reorderingBuffer, TargetCoreOptions.UsesAsyncCompletion); } @@ -103,7 +103,7 @@ private void Initialize( ref ReorderingBuffer>? reorderingBuffer, TargetCoreOptions targetCoreOptions) { - if (dataflowBlockOptions == null) throw new ArgumentNullException(nameof(dataflowBlockOptions)); + ArgumentNullException.ThrowIfNull(dataflowBlockOptions); // Ensure we have options that can't be changed by the caller dataflowBlockOptions = dataflowBlockOptions.DefaultOrClone(); diff --git a/src/libraries/System.Threading.Tasks.Dataflow/src/Blocks/WriteOnceBlock.cs b/src/libraries/System.Threading.Tasks.Dataflow/src/Blocks/WriteOnceBlock.cs index 23c49c2e99ce91..d0b83463337047 100644 --- a/src/libraries/System.Threading.Tasks.Dataflow/src/Blocks/WriteOnceBlock.cs +++ b/src/libraries/System.Threading.Tasks.Dataflow/src/Blocks/WriteOnceBlock.cs @@ -381,7 +381,7 @@ DataflowMessageStatus ITargetBlock.OfferMessage(DataflowMessageHeader message { // Validate arguments if (!messageHeader.IsValid) throw new ArgumentException(SR.Argument_InvalidMessageHeader, nameof(messageHeader)); - if (target == null) throw new ArgumentNullException(nameof(target)); + ArgumentNullException.ThrowIfNull(target); // As long as the message being requested is the one we have, allow it to be consumed, // but make a copy using the provided cloning function. @@ -402,7 +402,7 @@ bool ISourceBlock.ReserveMessage(DataflowMessageHeader messageHeader, ITarget { // Validate arguments if (!messageHeader.IsValid) throw new ArgumentException(SR.Argument_InvalidMessageHeader, nameof(messageHeader)); - if (target == null) throw new ArgumentNullException(nameof(target)); + ArgumentNullException.ThrowIfNull(target); // As long as the message is the one we have, it can be "reserved." // Reservations on a WriteOnceBlock are not exclusive, because @@ -415,7 +415,7 @@ void ISourceBlock.ReleaseReservation(DataflowMessageHeader messageHeader, ITa { // Validate arguments if (!messageHeader.IsValid) throw new ArgumentException(SR.Argument_InvalidMessageHeader, nameof(messageHeader)); - if (target == null) throw new ArgumentNullException(nameof(target)); + ArgumentNullException.ThrowIfNull(target); // As long as the message is the one we have, everything's fine. if (_header.Id != messageHeader.Id) throw new InvalidOperationException(SR.InvalidOperation_MessageNotReservedByTarget); diff --git a/src/libraries/System.Threading.Tasks.Dataflow/src/Internal/SourceCore.cs b/src/libraries/System.Threading.Tasks.Dataflow/src/Internal/SourceCore.cs index 128111df5c2d8d..ec14c7c3b27e3d 100644 --- a/src/libraries/System.Threading.Tasks.Dataflow/src/Internal/SourceCore.cs +++ b/src/libraries/System.Threading.Tasks.Dataflow/src/Internal/SourceCore.cs @@ -158,7 +158,7 @@ internal IDisposable LinkTo(ITargetBlock target, DataflowLinkOptions li { // Validate arguments if (!messageHeader.IsValid) throw new ArgumentException(SR.Argument_InvalidMessageHeader, nameof(messageHeader)); - if (target == null) throw new ArgumentNullException(nameof(target)); + ArgumentNullException.ThrowIfNull(target); TOutput? consumedMessageValue = default(TOutput); @@ -214,7 +214,7 @@ internal bool ReserveMessage(DataflowMessageHeader messageHeader, ITargetBlock + From c630e46919d4992a9c2ff30224fa0498a80b83c5 Mon Sep 17 00:00:00 2001 From: Eirik Tsarpalis Date: Wed, 21 Oct 2026 23:10:52 +0300 Subject: [PATCH 5/9] Remove redundant array-reverse range delegation The only caller, ImmutableArray.Builder.Reverse, supplies a valid range. Keep the null check and let AsSpan perform any range validation. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 3c692d8a-03a2-4ed9-b337-2066c25a3cb6 --- src/libraries/Common/src/Polyfills/ArrayPolyfills.cs | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/libraries/Common/src/Polyfills/ArrayPolyfills.cs b/src/libraries/Common/src/Polyfills/ArrayPolyfills.cs index 14dd0ccb9fbf2c..bbb86d787787e1 100644 --- a/src/libraries/Common/src/Polyfills/ArrayPolyfills.cs +++ b/src/libraries/Common/src/Polyfills/ArrayPolyfills.cs @@ -17,13 +17,6 @@ public static void Reverse(T[] array, int index, int length) throw new ArgumentNullException(nameof(array)); } - if (index < 0 || length < 0 || array.Length - index < length) - { - // Preserve the existing overload's argument validation. - Array.Reverse(array, index, length); - return; - } - array.AsSpan(index, length).Reverse(); } From 4661e533bf2fd5e88182d0b9194648c12870f813 Mon Sep 17 00:00:00 2001 From: Eirik Tsarpalis Date: Wed, 21 Oct 2026 23:21:14 +0300 Subject: [PATCH 6/9] Drop the generic Array.Reverse polyfill Restore ImmutableArray.Builder.Reverse and ArrayPolyfills to their original implementations. The span-based shim changes behavior for covariant arrays, so leave this migration out of the compatibility cleanup. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 3c692d8a-03a2-4ed9-b337-2066c25a3cb6 --- .../Common/src/Polyfills/ArrayPolyfills.cs | 10 ---------- .../Immutable/ImmutableArray_1.Builder.cs | 16 ++++++++++++++++ 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/src/libraries/Common/src/Polyfills/ArrayPolyfills.cs b/src/libraries/Common/src/Polyfills/ArrayPolyfills.cs index bbb86d787787e1..b0b31c47b5a0bb 100644 --- a/src/libraries/Common/src/Polyfills/ArrayPolyfills.cs +++ b/src/libraries/Common/src/Polyfills/ArrayPolyfills.cs @@ -10,16 +10,6 @@ internal static class ArrayPolyfills { public static int MaxLength => 0x7FFFFFC7; - public static void Reverse(T[] array, int index, int length) - { - if (array is null) - { - throw new ArgumentNullException(nameof(array)); - } - - array.AsSpan(index, length).Reverse(); - } - public static void Clear(Array array) { if (array is null) diff --git a/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableArray_1.Builder.cs b/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableArray_1.Builder.cs index a4d8c7f7ce9015..5d414f4249796c 100644 --- a/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableArray_1.Builder.cs +++ b/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableArray_1.Builder.cs @@ -914,7 +914,23 @@ public int LastIndexOf(T item, int startIndex, int count, IEqualityComparer? /// public void Reverse() { +#if NET || NETSTANDARD2_1_OR_GREATER Array.Reverse(_elements, 0, _count); +#else + // The non-generic Array.Reverse is not used because it does not perform + // well for non-primitive value types. + int i = 0; + int j = _count - 1; + T[] array = _elements; + while (i < j) + { + T temp = array[i]; + array[i] = array[j]; + array[j] = temp; + i++; + j--; + } +#endif } /// From 5d4a3dc5fe7074bb605cdc4511f1b4d601ca7c07 Mon Sep 17 00:00:00 2001 From: Eirik Tsarpalis Date: Thu, 22 Oct 2026 10:24:48 +0300 Subject: [PATCH 7/9] Restore array reversal polyfill with direct swaps Move the existing array-swap loop into the downlevel Array.Reverse polyfill. Preserve covariant arrays and delegate invalid inputs to the existing BCL overload. Use the generic API spelling in ImmutableArray.Builder.Reverse. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 3c692d8a-03a2-4ed9-b337-2066c25a3cb6 --- .../Common/src/Polyfills/ArrayPolyfills.cs | 20 +++++++++++++++++++ .../Immutable/ImmutableArray_1.Builder.cs | 16 --------------- 2 files changed, 20 insertions(+), 16 deletions(-) diff --git a/src/libraries/Common/src/Polyfills/ArrayPolyfills.cs b/src/libraries/Common/src/Polyfills/ArrayPolyfills.cs index b0b31c47b5a0bb..dc4331e3d1cbf1 100644 --- a/src/libraries/Common/src/Polyfills/ArrayPolyfills.cs +++ b/src/libraries/Common/src/Polyfills/ArrayPolyfills.cs @@ -10,6 +10,26 @@ internal static class ArrayPolyfills { public static int MaxLength => 0x7FFFFFC7; + public static void Reverse(T[] array, int index, int length) + { + if (array is null || index < 0 || length < 0 || array.Length - index < length) + { + Array.Reverse(array, index, length); + return; + } + + int i = index; + int j = index + length - 1; + while (i < j) + { + T temp = array[i]; + array[i] = array[j]; + array[j] = temp; + i++; + j--; + } + } + public static void Clear(Array array) { if (array is null) diff --git a/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableArray_1.Builder.cs b/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableArray_1.Builder.cs index 5d414f4249796c..a4d8c7f7ce9015 100644 --- a/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableArray_1.Builder.cs +++ b/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableArray_1.Builder.cs @@ -914,23 +914,7 @@ public int LastIndexOf(T item, int startIndex, int count, IEqualityComparer? /// public void Reverse() { -#if NET || NETSTANDARD2_1_OR_GREATER Array.Reverse(_elements, 0, _count); -#else - // The non-generic Array.Reverse is not used because it does not perform - // well for non-primitive value types. - int i = 0; - int j = _count - 1; - T[] array = _elements; - while (i < j) - { - T temp = array[i]; - array[i] = array[j]; - array[j] = temp; - i++; - j--; - } -#endif } /// From ff704ea9db1e951f0bedfa2a0e34b81a28148069 Mon Sep 17 00:00:00 2001 From: Eirik Tsarpalis Date: Thu, 22 Oct 2026 10:48:26 +0300 Subject: [PATCH 8/9] Keep array reversal polyfill faithful to the original loop Remove the extra validation and non-generic Array.Reverse delegation. The reversal polyfill now contains only the original array-swap loop. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 3c692d8a-03a2-4ed9-b337-2066c25a3cb6 --- src/libraries/Common/src/Polyfills/ArrayPolyfills.cs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/libraries/Common/src/Polyfills/ArrayPolyfills.cs b/src/libraries/Common/src/Polyfills/ArrayPolyfills.cs index dc4331e3d1cbf1..88feb836e93652 100644 --- a/src/libraries/Common/src/Polyfills/ArrayPolyfills.cs +++ b/src/libraries/Common/src/Polyfills/ArrayPolyfills.cs @@ -12,12 +12,6 @@ internal static class ArrayPolyfills public static void Reverse(T[] array, int index, int length) { - if (array is null || index < 0 || length < 0 || array.Length - index < length) - { - Array.Reverse(array, index, length); - return; - } - int i = index; int j = index + length - 1; while (i < j) From e3bd647a859eac283ef9b456c604662c3e5c35ae Mon Sep 17 00:00:00 2001 From: Eirik Tsarpalis Date: Thu, 22 Oct 2026 12:18:44 +0300 Subject: [PATCH 9/9] Validate array reversal polyfill arguments directly Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 3c692d8a-03a2-4ed9-b337-2066c25a3cb6 --- .../Common/src/Polyfills/ArrayPolyfills.cs | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/libraries/Common/src/Polyfills/ArrayPolyfills.cs b/src/libraries/Common/src/Polyfills/ArrayPolyfills.cs index 88feb836e93652..c45282cd1c56ca 100644 --- a/src/libraries/Common/src/Polyfills/ArrayPolyfills.cs +++ b/src/libraries/Common/src/Polyfills/ArrayPolyfills.cs @@ -12,6 +12,26 @@ internal static class ArrayPolyfills public static void Reverse(T[] array, int index, int length) { + if (array is null) + { + throw new ArgumentNullException(nameof(array)); + } + + if (index < 0) + { + throw new ArgumentOutOfRangeException(nameof(index)); + } + + if (length < 0) + { + throw new ArgumentOutOfRangeException(nameof(length)); + } + + if (array.Length - index < length) + { + throw new ArgumentException(); + } + int i = index; int j = index + length - 1; while (i < j)