From a9a0bc658f7808ba7816c6c3658ba965b50667ed Mon Sep 17 00:00:00 2001 From: Jan Kotas Date: Sun, 4 Mar 2018 20:05:50 -0800 Subject: [PATCH] Fix encoding methods for Span.Empty (#16748) * 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 --- .../Runtime/InteropServices/MemoryMarshal.Fast.cs | 20 +++++++++++++++++--- src/mscorlib/shared/System/Text/Decoder.cs | 10 +++++----- src/mscorlib/shared/System/Text/Encoder.cs | 10 +++++----- src/mscorlib/shared/System/Text/Encoding.cs | 14 +++++++------- 4 files changed, 34 insertions(+), 20 deletions(-) diff --git a/src/mscorlib/shared/System/Runtime/InteropServices/MemoryMarshal.Fast.cs b/src/mscorlib/shared/System/Runtime/InteropServices/MemoryMarshal.Fast.cs index 5d2a444..34881e7 100644 --- a/src/mscorlib/shared/System/Runtime/InteropServices/MemoryMarshal.Fast.cs +++ b/src/mscorlib/shared/System/Runtime/InteropServices/MemoryMarshal.Fast.cs @@ -29,17 +29,31 @@ namespace System.Runtime.InteropServices /// /// 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. /// public static ref T GetReference(Span span) => ref span._pointer.Value; /// - /// 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. /// public static ref T GetReference(ReadOnlySpan span) => ref span._pointer.Value; /// + /// 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. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static unsafe ref T GetNonNullPinnableReference(Span span) => ref (span.Length != 0) ? ref span._pointer.Value : ref Unsafe.AsRef((void*)1); + + /// + /// 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. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static unsafe ref T GetNonNullPinnableReference(ReadOnlySpan span) => ref (span.Length != 0) ? ref span._pointer.Value : ref Unsafe.AsRef((void*)1); + + /// /// Casts a Span of one primitive type to another primitive type . /// These types may not contain pointers or references. This is checked at runtime in order to preserve type safety. /// diff --git a/src/mscorlib/shared/System/Text/Decoder.cs b/src/mscorlib/shared/System/Text/Decoder.cs index b827648..b4a7575 100644 --- a/src/mscorlib/shared/System/Text/Decoder.cs +++ b/src/mscorlib/shared/System/Text/Decoder.cs @@ -134,7 +134,7 @@ namespace System.Text public virtual unsafe int GetCharCount(ReadOnlySpan 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 bytes, Span 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 bytes, Span 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); } diff --git a/src/mscorlib/shared/System/Text/Encoder.cs b/src/mscorlib/shared/System/Text/Encoder.cs index fb1bdb8..df7d512 100644 --- a/src/mscorlib/shared/System/Text/Encoder.cs +++ b/src/mscorlib/shared/System/Text/Encoder.cs @@ -132,7 +132,7 @@ namespace System.Text public virtual unsafe int GetByteCount(ReadOnlySpan 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 chars, Span 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 chars, Span 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); } diff --git a/src/mscorlib/shared/System/Text/Encoding.cs b/src/mscorlib/shared/System/Text/Encoding.cs index e469180..e191ce1 100644 --- a/src/mscorlib/shared/System/Text/Encoding.cs +++ b/src/mscorlib/shared/System/Text/Encoding.cs @@ -713,7 +713,7 @@ namespace System.Text public virtual unsafe int GetByteCount(ReadOnlySpan 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 chars, Span 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 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 bytes, Span 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 bytes) { - fixed (byte* bytesPtr = &MemoryMarshal.GetReference(bytes)) + fixed (byte* bytesPtr = &MemoryMarshal.GetNonNullPinnableReference(bytes)) { return GetString(bytesPtr, bytes.Length); } -- 2.7.4