diff --git a/src/libraries/Common/src/SourceGenerators/SourceWriter.cs b/src/libraries/Common/src/SourceGenerators/SourceWriter.cs index 0cc443d86fc3fd..d1307be214f1c1 100644 --- a/src/libraries/Common/src/SourceGenerators/SourceWriter.cs +++ b/src/libraries/Common/src/SourceGenerators/SourceWriter.cs @@ -112,6 +112,7 @@ private static ReadOnlySpan GetNextLine(ref ReadOnlySpan remainingTe private static unsafe void AppendSpan(StringBuilder builder, ReadOnlySpan span) { + // There is no StringBuilder.Append(ReadOnlySpan) overload in the NS2.0 fixed (char* ptr = span) { builder.Append(ptr, span.Length); diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Document/JsonDocument.DbRow.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Document/JsonDocument.DbRow.cs index 1a391e3ebdc53f..8894abd7e71227 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Document/JsonDocument.DbRow.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Document/JsonDocument.DbRow.cs @@ -3,6 +3,7 @@ using System.Diagnostics; using System.Runtime.InteropServices; +using System.Runtime.CompilerServices; namespace System.Text.Json { @@ -51,19 +52,13 @@ internal readonly struct DbRow internal const int UnknownSize = -1; -#if DEBUG - static unsafe DbRow() - { - Debug.Assert(sizeof(DbRow) == Size); - } -#endif - internal DbRow(JsonTokenType jsonTokenType, int location, int sizeOrLength) { Debug.Assert(jsonTokenType > JsonTokenType.None && jsonTokenType <= JsonTokenType.Null); Debug.Assert((byte)jsonTokenType < 1 << 4); Debug.Assert(location >= 0); Debug.Assert(sizeOrLength >= UnknownSize); + Debug.Assert(Unsafe.SizeOf() == Size); _location = location; _sizeOrLengthUnion = sizeOrLength; diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Document/JsonElement.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Document/JsonElement.cs index e71c9ae3aa9fe9..ad561c5dbff392 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Document/JsonElement.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Document/JsonElement.cs @@ -1476,7 +1476,9 @@ public bool ValueEquals(ReadOnlySpan utf8Text) if (TokenType == JsonTokenType.Null) { // This is different than Length == 0, in that it tests true for null, but false for "" - return Unsafe.IsNullRef(ref MemoryMarshal.GetReference(utf8Text)); +#pragma warning disable CA2265 + return utf8Text.Slice(0, 0) == default; +#pragma warning restore CA2265 } return TextEqualsHelper(utf8Text, isPropertyName: false, shouldUnescape: true); @@ -1504,7 +1506,9 @@ public bool ValueEquals(ReadOnlySpan text) if (TokenType == JsonTokenType.Null) { // This is different than Length == 0, in that it tests true for null, but false for "" - return Unsafe.IsNullRef(ref MemoryMarshal.GetReference(text)); +#pragma warning disable CA2265 + return text.Slice(0, 0) == default; +#pragma warning restore CA2265 } return TextEqualsHelper(text, isPropertyName: false); 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 fa3186f0096995..d6e1d6cbd3a301 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 @@ -113,14 +113,14 @@ public override T Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerial case JsonTokenType.Number when (_converterOptions & EnumConverterOptions.AllowNumbers) != 0: switch (s_enumTypeCode) { - case TypeCode.Int32 when reader.TryGetInt32(out int int32): return Unsafe.As(ref int32); - case TypeCode.UInt32 when reader.TryGetUInt32(out uint uint32): return Unsafe.As(ref uint32); - case TypeCode.Int64 when reader.TryGetInt64(out long int64): return Unsafe.As(ref int64); - case TypeCode.UInt64 when reader.TryGetUInt64(out ulong uint64): return Unsafe.As(ref uint64); - case TypeCode.Byte when reader.TryGetByte(out byte ubyte8): return Unsafe.As(ref ubyte8); - case TypeCode.SByte when reader.TryGetSByte(out sbyte byte8): return Unsafe.As(ref byte8); - case TypeCode.Int16 when reader.TryGetInt16(out short int16): return Unsafe.As(ref int16); - case TypeCode.UInt16 when reader.TryGetUInt16(out ushort uint16): return Unsafe.As(ref uint16); + case TypeCode.Int32 when reader.TryGetInt32(out int int32): return (T)(object)int32; + case TypeCode.UInt32 when reader.TryGetUInt32(out uint uint32): return (T)(object)uint32; + case TypeCode.Int64 when reader.TryGetInt64(out long int64): return (T)(object)int64; + case TypeCode.UInt64 when reader.TryGetUInt64(out ulong uint64): return (T)(object)uint64; + case TypeCode.Byte when reader.TryGetByte(out byte ubyte8): return (T)(object)ubyte8; + case TypeCode.SByte when reader.TryGetSByte(out sbyte byte8): return (T)(object)byte8; + case TypeCode.Int16 when reader.TryGetInt16(out short int16): return (T)(object)int16; + case TypeCode.UInt16 when reader.TryGetUInt16(out ushort uint16): return (T)(object)uint16; } break; } @@ -350,51 +350,43 @@ private bool TryParseNamedEnum( private static ulong ConvertToUInt64(T value) { - switch (s_enumTypeCode) - { - case TypeCode.Int32 or TypeCode.UInt32: return Unsafe.As(ref value); - case TypeCode.Int64 or TypeCode.UInt64: return Unsafe.As(ref value); - case TypeCode.Int16 or TypeCode.UInt16: return Unsafe.As(ref value); - default: - Debug.Assert(s_enumTypeCode is TypeCode.SByte or TypeCode.Byte); - return Unsafe.As(ref value); + return s_enumTypeCode switch + { + TypeCode.Int32 => (ulong)(int)(object)value, + TypeCode.UInt32 => (uint)(object)value, + TypeCode.Int64 => (ulong)(long)(object)value, + TypeCode.UInt64 => (ulong)(object)value, + TypeCode.Int16 => (ulong)(short)(object)value, + TypeCode.UInt16 => (ushort)(object)value, + TypeCode.SByte => (ulong)(sbyte)(object)value, + _ => (byte)(object)value }; } private static long ConvertToInt64(T value) { Debug.Assert(s_isSignedEnum); - switch (s_enumTypeCode) - { - case TypeCode.Int32: return Unsafe.As(ref value); - case TypeCode.Int64: return Unsafe.As(ref value); - case TypeCode.Int16: return Unsafe.As(ref value); - default: - Debug.Assert(s_enumTypeCode is TypeCode.SByte); - return Unsafe.As(ref value); + return s_enumTypeCode switch + { + TypeCode.Int32 => (int)(object)value, + TypeCode.Int64 => (long)(object)value, + TypeCode.Int16 => (short)(object)value, + _ => (sbyte)(object)value, }; } private static T ConvertFromUInt64(ulong value) { - switch (s_enumTypeCode) - { - case TypeCode.Int32 or TypeCode.UInt32: - uint uintValue = (uint)value; - return Unsafe.As(ref uintValue); - - case TypeCode.Int64 or TypeCode.UInt64: - ulong ulongValue = value; - return Unsafe.As(ref ulongValue); - - case TypeCode.Int16 or TypeCode.UInt16: - ushort ushortValue = (ushort)value; - return Unsafe.As(ref ushortValue); - - default: - Debug.Assert(s_enumTypeCode is TypeCode.SByte or TypeCode.Byte); - byte byteValue = (byte)value; - return Unsafe.As(ref byteValue); + return s_enumTypeCode switch + { + TypeCode.Int32 => (T)(object)(int)value, + TypeCode.UInt32 => (T)(object)(uint)value, + TypeCode.Int64 => (T)(object)(long)value, + TypeCode.UInt64 => (T)(object)value, + TypeCode.Int16 => (T)(object)(short)value, + TypeCode.UInt16 => (T)(object)(ushort)value, + TypeCode.SByte => (T)(object)(sbyte)value, + _ => (T)(object)(byte)value }; } diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonTypeInfo.Cache.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonTypeInfo.Cache.cs index 13f0821f8b32d5..3fec42d3518c7e 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonTypeInfo.Cache.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonTypeInfo.Cache.cs @@ -73,7 +73,6 @@ internal Dictionary PropertyIndex /// /// Defines the core property lookup logic for a given unescaped UTF-8 encoded property name. /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] internal JsonPropertyInfo? GetProperty(ReadOnlySpan propertyName, ref ReadStackFrame frame, out byte[] utf8PropertyName) { Debug.Assert(IsConfigured); diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/PropertyRef.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/PropertyRef.cs index d1f4c0afaf8dc9..aabcfdd2ee5f9f 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/PropertyRef.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/PropertyRef.cs @@ -52,28 +52,20 @@ public bool Equals(ReadOnlySpan propertyName, ulong key) [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ulong GetKey(ReadOnlySpan name) { - ref byte reference = ref MemoryMarshal.GetReference(name); int length = name.Length; ulong key = (ulong)(byte)length << 56; - - switch (length) + key |= length switch { - case 0: goto ComputedKey; - case 1: goto OddLength; - case 2: key |= Unsafe.ReadUnaligned(ref reference); goto ComputedKey; - case 3: key |= Unsafe.ReadUnaligned(ref reference); goto OddLength; - case 4: key |= Unsafe.ReadUnaligned(ref reference); goto ComputedKey; - case 5: key |= Unsafe.ReadUnaligned(ref reference); goto OddLength; - case 6: key |= Unsafe.ReadUnaligned(ref reference) | (ulong)Unsafe.ReadUnaligned(ref Unsafe.Add(ref reference, 4)) << 32; goto ComputedKey; - case 7: key |= Unsafe.ReadUnaligned(ref reference) | (ulong)Unsafe.ReadUnaligned(ref Unsafe.Add(ref reference, 4)) << 32; goto OddLength; - default: key |= Unsafe.ReadUnaligned(ref reference) & 0x00ffffffffffffffL; goto ComputedKey; - } - - OddLength: - int offset = length - 1; - key |= (ulong)Unsafe.Add(ref reference, offset) << (offset * 8); - - ComputedKey: + 0 => 0, + 1 => name[0], + 2 => MemoryMarshal.Read(name), + 3 => MemoryMarshal.Read(name) | ((ulong)name[2] << 16), + 4 => MemoryMarshal.Read(name), + 5 => MemoryMarshal.Read(name) | ((ulong)name[4] << 32), + 6 => MemoryMarshal.Read(name) | ((ulong)MemoryMarshal.Read(name.Slice(4, 2)) << 32), + 7 => MemoryMarshal.Read(name) | ((ulong)MemoryMarshal.Read(name.Slice(4, 2)) << 32) | ((ulong)name[6] << 48), + _ => MemoryMarshal.Read(name) & 0x00ffffffffffffffUL + }; #if DEBUG // Verify key contains the embedded bytes as expected. // Note: the expected properties do not hold true on big-endian platforms diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Writer/JsonWriterHelper.Escaping.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Writer/JsonWriterHelper.Escaping.cs index 3010e31cbd6fd8..cb2e6e1c81feeb 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Writer/JsonWriterHelper.Escaping.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Writer/JsonWriterHelper.Escaping.cs @@ -57,7 +57,7 @@ public static int NeedsEscaping(ReadOnlySpan value, JavaScriptEncoder? enc return (encoder ?? JavaScriptEncoder.Default).FindFirstCharacterToEncodeUtf8(value); } - public static unsafe int NeedsEscaping(ReadOnlySpan value, JavaScriptEncoder? encoder) + public static int NeedsEscaping(ReadOnlySpan value, JavaScriptEncoder? encoder) { // Some implementations of JavaScriptEncoder.FindFirstCharacterToEncode may not accept // null pointers and guard against that. Hence, check up-front to return -1. @@ -66,9 +66,14 @@ public static unsafe int NeedsEscaping(ReadOnlySpan value, JavaScriptEncod return -1; } - fixed (char* ptr = value) + // Unfortunately, there is no public API for FindFirstCharacterToEncode(Span) yet, + // so we have to use the unsafe FindFirstCharacterToEncode(char*, int) instead. + unsafe { - return (encoder ?? JavaScriptEncoder.Default).FindFirstCharacterToEncode(ptr, value.Length); + fixed (char* ptr = value) + { + return (encoder ?? JavaScriptEncoder.Default).FindFirstCharacterToEncode(ptr, value.Length); + } } } 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 b2e05f589fc10c..8844132888beee 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 @@ -257,7 +257,7 @@ internal static void ValidateNumber(ReadOnlySpan utf8FormattedNumber) private static readonly UTF8Encoding s_utf8Encoding = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true); #endif - public static unsafe bool IsValidUtf8String(ReadOnlySpan bytes) + public static bool IsValidUtf8String(ReadOnlySpan bytes) { #if NET8_0_OR_GREATER return Utf8.IsValid(bytes); @@ -269,9 +269,12 @@ public static unsafe bool IsValidUtf8String(ReadOnlySpan bytes) #else if (!bytes.IsEmpty) { - fixed (byte* ptr = bytes) + unsafe { - s_utf8Encoding.GetCharCount(ptr, bytes.Length); + fixed (byte* ptr = bytes) + { + s_utf8Encoding.GetCharCount(ptr, bytes.Length); + } } } #endif @@ -284,7 +287,7 @@ public static unsafe bool IsValidUtf8String(ReadOnlySpan bytes) #endif } - internal static unsafe OperationStatus ToUtf8(ReadOnlySpan source, Span destination, out int written) + internal static OperationStatus ToUtf8(ReadOnlySpan source, Span destination, out int written) { #if NET OperationStatus status = Utf8.FromUtf16(source, destination, out int charsRead, out written, replaceInvalidSequences: false, isFinalBlock: true); @@ -297,10 +300,13 @@ internal static unsafe OperationStatus ToUtf8(ReadOnlySpan source, Span span) { + // There is no StringBuilder.Append(ReadOnlySpan) overload in the NS2.0 fixed (char* ptr = &MemoryMarshal.GetReference(span)) { return stringBuilder.Append(ptr, span.Length); diff --git a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexCharClass.cs b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexCharClass.cs index 8a4acef3785be3..a2c5e98e0ed3fb 100644 --- a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexCharClass.cs +++ b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexCharClass.cs @@ -1543,7 +1543,7 @@ private static RegexCharClass ParseRecursive(string charClass, int start) public static string OneToStringClass(char c) => CharsToStringClass([c]); - internal static unsafe string CharsToStringClass(ReadOnlySpan chars) + internal static string CharsToStringClass(ReadOnlySpan chars) { #if DEBUG // Make sure they're all sorted with no duplicates @@ -1592,20 +1592,15 @@ internal static unsafe string CharsToStringClass(ReadOnlySpan chars) // Get the pointer/length of the span to be able to pass it into string.Create. ReadOnlySpan tmpChars = chars; // avoid address exposing the span and impacting the other code in the method that uses it - return #if NET - string -#else - StringExtensions -#endif - .Create(SetStartIndex + count, (IntPtr)(&tmpChars), static (span, charsPtr) => + return string.Create(SetStartIndex + count, tmpChars, static (span, chars) => { // Fill in the set string span[FlagsIndex] = (char)0; span[SetLengthIndex] = (char)(span.Length - SetStartIndex); span[CategoryLengthIndex] = (char)0; int i = SetStartIndex; - foreach (char c in *(ReadOnlySpan*)charsPtr) + foreach (char c in chars) { span[i++] = c; if (c != LastChar) @@ -1615,8 +1610,32 @@ internal static unsafe string CharsToStringClass(ReadOnlySpan chars) } Debug.Assert(i == span.Length); }); +#else + unsafe + { + return StringExtensions.Create(SetStartIndex + count, (IntPtr)(&tmpChars), static (span, charsPtr) => + { + // Fill in the set string + span[FlagsIndex] = (char)0; + span[SetLengthIndex] = (char)(span.Length - SetStartIndex); + span[CategoryLengthIndex] = (char)0; + int i = SetStartIndex; + ReadOnlySpan chars = *(ReadOnlySpan*)charsPtr; + foreach (char c in chars) + { + span[i++] = c; + if (c != LastChar) + { + span[i++] = (char)(c + 1); + } + } + Debug.Assert(i == span.Length); + }); + } +#endif } + /// /// Constructs the string representation of the class. ///