Fix encoding methods for Span.Empty (#16748)
authorJan Kotas <jkotas@microsoft.com>
Mon, 5 Mar 2018 04:05:50 +0000 (20:05 -0800)
committerGitHub <noreply@github.com>
Mon, 5 Mar 2018 04:05:50 +0000 (20:05 -0800)
* Fix encoding methods for Span.Empty

Encoding had a historic confusion about handling null pointers. Make sure that this confusion is not leaking into the new Span methods.

* Use GetNonNullPinnableReference helper method

src/mscorlib/shared/System/Runtime/InteropServices/MemoryMarshal.Fast.cs
src/mscorlib/shared/System/Text/Decoder.cs
src/mscorlib/shared/System/Text/Encoder.cs
src/mscorlib/shared/System/Text/Encoding.cs

index 5d2a444..34881e7 100644 (file)
@@ -29,17 +29,31 @@ namespace System.Runtime.InteropServices
 
         /// <summary>
         /// Returns a reference to the 0th element of the Span. If the Span is empty, returns a reference to the location where the 0th element
-        /// would have been stored. Such a reference can be used for pinning but must never be dereferenced.
+        /// would have been stored. Such a reference may or may not be null. It can be used for pinning but must never be dereferenced.
         /// </summary>
         public static ref T GetReference<T>(Span<T> span) => ref span._pointer.Value;
 
         /// <summary>
-        /// Returns a reference to the 0th element of the ReadOnlySpan. If the Span is empty, returns a reference to the location where the 0th element
-        /// would have been stored. Such a reference can be used for pinning but must never be dereferenced.
+        /// Returns a reference to the 0th element of the ReadOnlySpan. If the ReadOnlySpan is empty, returns a reference to the location where the 0th element
+        /// would have been stored. Such a reference may or may not be null. It can be used for pinning but must never be dereferenced.
         /// </summary>
         public static ref T GetReference<T>(ReadOnlySpan<T> span) => ref span._pointer.Value;
 
         /// <summary>
+        /// Returns a reference to the 0th element of the Span. If the Span is empty, returns a reference to fake non-null pointer. Such a reference can be used
+        /// for pinning but must never be dereferenced. This is useful for interop with methods that do not accept null pointers for zero-sized buffers.
+        /// </summary>
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        internal static unsafe ref T GetNonNullPinnableReference<T>(Span<T> span) => ref (span.Length != 0) ? ref span._pointer.Value : ref Unsafe.AsRef<T>((void*)1);
+
+        /// <summary>
+        /// Returns a reference to the 0th element of the ReadOnlySpan. If the ReadOnlySpan is empty, returns a reference to fake non-null pointer. Such a reference
+        /// can be used for pinning but must never be dereferenced. This is useful for interop with methods that do not accept null pointers for zero-sized buffers.
+        /// </summary>
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        internal static unsafe ref T GetNonNullPinnableReference<T>(ReadOnlySpan<T> span) => ref (span.Length != 0) ? ref span._pointer.Value : ref Unsafe.AsRef<T>((void*)1);
+
+        /// <summary>
         /// Casts a Span of one primitive type <typeparamref name="TFrom"/> to another primitive type <typeparamref name="TTo"/>.
         /// These types may not contain pointers or references. This is checked at runtime in order to preserve type safety.
         /// </summary>
index b827648..b4a7575 100644 (file)
@@ -134,7 +134,7 @@ namespace System.Text
 
         public virtual unsafe int GetCharCount(ReadOnlySpan<byte> bytes, bool flush)
         {
-            fixed (byte* bytesPtr = &MemoryMarshal.GetReference(bytes))
+            fixed (byte* bytesPtr = &MemoryMarshal.GetNonNullPinnableReference(bytes))
             {
                 return GetCharCount(bytesPtr, bytes.Length, flush);
             }
@@ -227,8 +227,8 @@ namespace System.Text
 
         public virtual unsafe int GetChars(ReadOnlySpan<byte> bytes, Span<char> chars, bool flush)
         {
-            fixed (byte* bytesPtr = &MemoryMarshal.GetReference(bytes))
-            fixed (char* charsPtr = &MemoryMarshal.GetReference(chars))
+            fixed (byte* bytesPtr = &MemoryMarshal.GetNonNullPinnableReference(bytes))
+            fixed (char* charsPtr = &MemoryMarshal.GetNonNullPinnableReference(chars))
             {
                 return GetChars(bytesPtr, bytes.Length, charsPtr, chars.Length, flush);
             }
@@ -341,8 +341,8 @@ namespace System.Text
 
         public virtual unsafe void Convert(ReadOnlySpan<byte> bytes, Span<char> chars, bool flush, out int bytesUsed, out int charsUsed, out bool completed)
         {
-            fixed (byte* bytesPtr = &MemoryMarshal.GetReference(bytes))
-            fixed (char* charsPtr = &MemoryMarshal.GetReference(chars))
+            fixed (byte* bytesPtr = &MemoryMarshal.GetNonNullPinnableReference(bytes))
+            fixed (char* charsPtr = &MemoryMarshal.GetNonNullPinnableReference(chars))
             {
                 Convert(bytesPtr, bytes.Length, charsPtr, chars.Length, flush, out bytesUsed, out charsUsed, out completed);
             }
index fb1bdb8..df7d512 100644 (file)
@@ -132,7 +132,7 @@ namespace System.Text
 
         public virtual unsafe int GetByteCount(ReadOnlySpan<char> chars, bool flush)
         {
-            fixed (char* charsPtr = &MemoryMarshal.GetReference(chars))
+            fixed (char* charsPtr = &MemoryMarshal.GetNonNullPinnableReference(chars))
             {
                 return GetByteCount(charsPtr, chars.Length, flush);
             }
@@ -221,8 +221,8 @@ namespace System.Text
 
         public virtual unsafe int GetBytes(ReadOnlySpan<char> chars, Span<byte> bytes, bool flush)
         {
-            fixed (char* charsPtr = &MemoryMarshal.GetReference(chars))
-            fixed (byte* bytesPtr = &MemoryMarshal.GetReference(bytes))
+            fixed (char* charsPtr = &MemoryMarshal.GetNonNullPinnableReference(chars))
+            fixed (byte* bytesPtr = &MemoryMarshal.GetNonNullPinnableReference(bytes))
             {
                 return GetBytes(charsPtr, chars.Length, bytesPtr, bytes.Length, flush);
             }
@@ -335,8 +335,8 @@ namespace System.Text
 
         public virtual unsafe void Convert(ReadOnlySpan<char> chars, Span<byte> bytes, bool flush, out int charsUsed, out int bytesUsed, out bool completed)
         {
-            fixed (char* charsPtr = &MemoryMarshal.GetReference(chars))
-            fixed (byte* bytesPtr = &MemoryMarshal.GetReference(bytes))
+            fixed (char* charsPtr = &MemoryMarshal.GetNonNullPinnableReference(chars))
+            fixed (byte* bytesPtr = &MemoryMarshal.GetNonNullPinnableReference(bytes))
             {
                 Convert(charsPtr, chars.Length, bytesPtr, bytes.Length, flush, out charsUsed, out bytesUsed, out completed);
             }
index e469180..e191ce1 100644 (file)
@@ -713,7 +713,7 @@ namespace System.Text
 
         public virtual unsafe int GetByteCount(ReadOnlySpan<char> chars)
         {
-            fixed (char* charsPtr = &MemoryMarshal.GetReference(chars))
+            fixed (char* charsPtr = &MemoryMarshal.GetNonNullPinnableReference(chars))
             {
                 return GetByteCount(charsPtr, chars.Length);
             }
@@ -895,8 +895,8 @@ namespace System.Text
 
         public virtual unsafe int GetBytes(ReadOnlySpan<char> chars, Span<byte> bytes)
         {
-            fixed (char* charsPtr = &MemoryMarshal.GetReference(chars))
-            fixed (byte* bytesPtr = &MemoryMarshal.GetReference(bytes))
+            fixed (char* charsPtr = &MemoryMarshal.GetNonNullPinnableReference(chars))
+            fixed (byte* bytesPtr = &MemoryMarshal.GetNonNullPinnableReference(bytes))
             {
                 return GetBytes(charsPtr, chars.Length, bytesPtr, bytes.Length);
             }
@@ -945,7 +945,7 @@ namespace System.Text
 
         public virtual unsafe int GetCharCount(ReadOnlySpan<byte> bytes)
         {
-            fixed (byte* bytesPtr = &MemoryMarshal.GetReference(bytes))
+            fixed (byte* bytesPtr = &MemoryMarshal.GetNonNullPinnableReference(bytes))
             {
                 return GetCharCount(bytesPtr, bytes.Length);
             }
@@ -1057,8 +1057,8 @@ namespace System.Text
 
         public virtual unsafe int GetChars(ReadOnlySpan<byte> bytes, Span<char> chars)
         {
-            fixed (byte* bytesPtr = &MemoryMarshal.GetReference(bytes))
-            fixed (char* charsPtr = &MemoryMarshal.GetReference(chars))
+            fixed (byte* bytesPtr = &MemoryMarshal.GetNonNullPinnableReference(bytes))
+            fixed (char* charsPtr = &MemoryMarshal.GetNonNullPinnableReference(chars))
             {
                 return GetChars(bytesPtr, bytes.Length, charsPtr, chars.Length);
             }
@@ -1087,7 +1087,7 @@ namespace System.Text
 
         public unsafe string GetString(ReadOnlySpan<byte> bytes)
         {
-            fixed (byte* bytesPtr = &MemoryMarshal.GetReference(bytes))
+            fixed (byte* bytesPtr = &MemoryMarshal.GetNonNullPinnableReference(bytes))
             {
                 return GetString(bytesPtr, bytes.Length);
             }