Moving Span APIs that allow skipping visibility checks to MemoryMarshal (#17087)
authorAhson Khan <ahkha@microsoft.com>
Wed, 21 Mar 2018 19:34:48 +0000 (12:34 -0700)
committerGitHub <noreply@github.com>
Wed, 21 Mar 2018 19:34:48 +0000 (12:34 -0700)
src/mscorlib/shared/System/Runtime/InteropServices/MemoryMarshal.Fast.cs
src/mscorlib/shared/System/Runtime/InteropServices/MemoryMarshal.cs

index a8dadea..80ae450 100644 (file)
@@ -15,6 +15,52 @@ namespace System.Runtime.InteropServices
     /// </summary>
     public static partial class MemoryMarshal
     {
+        /// <summary>
+        /// Casts a Span of one primitive type <typeparamref name="T"/> to Span of bytes.
+        /// That type may not contain pointers or references. This is checked at runtime in order to preserve type safety.
+        /// </summary>
+        /// <param name="span">The source slice, of type <typeparamref name="T"/>.</param>
+        /// <exception cref="System.ArgumentException">
+        /// Thrown when <typeparamref name="T"/> contains pointers.
+        /// </exception>
+        /// <exception cref="System.OverflowException">
+        /// Thrown if the Length property of the new Span would exceed Int32.MaxValue.
+        /// </exception>
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public static Span<byte> AsBytes<T>(Span<T> span)
+            where T : struct
+        {
+            if (RuntimeHelpers.IsReferenceOrContainsReferences<T>())
+                ThrowHelper.ThrowInvalidTypeWithPointersNotSupported(typeof(T));
+
+            return new Span<byte>(
+                ref Unsafe.As<T, byte>(ref GetReference(span)),
+                checked(span.Length * Unsafe.SizeOf<T>()));
+        }
+
+        /// <summary>
+        /// Casts a ReadOnlySpan of one primitive type <typeparamref name="T"/> to ReadOnlySpan of bytes.
+        /// That type may not contain pointers or references. This is checked at runtime in order to preserve type safety.
+        /// </summary>
+        /// <param name="span">The source slice, of type <typeparamref name="T"/>.</param>
+        /// <exception cref="System.ArgumentException">
+        /// Thrown when <typeparamref name="T"/> contains pointers.
+        /// </exception>
+        /// <exception cref="System.OverflowException">
+        /// Thrown if the Length property of the new Span would exceed Int32.MaxValue.
+        /// </exception>
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public static ReadOnlySpan<byte> AsBytes<T>(ReadOnlySpan<T> span)
+            where T : struct
+        {
+            if (RuntimeHelpers.IsReferenceOrContainsReferences<T>())
+                ThrowHelper.ThrowInvalidTypeWithPointersNotSupported(typeof(T));
+
+            return new ReadOnlySpan<byte>(
+                ref Unsafe.As<T, byte>(ref GetReference(span)),
+                checked(span.Length * Unsafe.SizeOf<T>()));
+        }
+
         /// <summary>Creates a <see cref="Memory{T}"/> from a <see cref="ReadOnlyMemory{T}"/>.</summary>
         /// <param name="memory">The <see cref="ReadOnlyMemory{T}"/>.</param>
         /// <returns>A <see cref="Memory{T}"/> representing the same memory as the <see cref="ReadOnlyMemory{T}"/>, but writable.</returns>
index 0f558d9..2c277e6 100644 (file)
@@ -6,6 +6,10 @@ using System.Buffers;
 using System.Runtime.CompilerServices;
 using System.Collections.Generic;
 
+#if !netstandard
+using Internal.Runtime.CompilerServices;
+#endif
+
 namespace System.Runtime.InteropServices
 {
     /// <summary>
@@ -122,5 +126,110 @@ namespace System.Runtime.InteropServices
                 return false;
             }
         }
+
+        /// <summary>
+        /// Reads a structure of type T out of a read-only span of bytes.
+        /// </summary>
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public static T ReadMachineEndian<T>(ReadOnlySpan<byte> source)
+            where T : struct
+        {
+#if netstandard
+            if (SpanHelpers.IsReferenceOrContainsReferences<T>())
+            {
+                ThrowHelper.ThrowArgumentException_InvalidTypeWithPointersNotSupported(typeof(T));
+            }
+#else
+            if (RuntimeHelpers.IsReferenceOrContainsReferences<T>())
+            {
+                ThrowHelper.ThrowInvalidTypeWithPointersNotSupported(typeof(T));
+            }
+#endif
+            if (Unsafe.SizeOf<T>() > source.Length)
+            {
+                ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.length);
+            }
+            return Unsafe.ReadUnaligned<T>(ref GetReference(source));
+        }
+
+        /// <summary>
+        /// Reads a structure of type T out of a span of bytes.
+        /// <returns>If the span is too small to contain the type T, return false.</returns>
+        /// </summary>
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public static bool TryReadMachineEndian<T>(ReadOnlySpan<byte> source, out T value)
+            where T : struct
+        {
+#if netstandard
+            if (SpanHelpers.IsReferenceOrContainsReferences<T>())
+            {
+                ThrowHelper.ThrowArgumentException_InvalidTypeWithPointersNotSupported(typeof(T));
+            }
+#else
+            if (RuntimeHelpers.IsReferenceOrContainsReferences<T>())
+            {
+                ThrowHelper.ThrowInvalidTypeWithPointersNotSupported(typeof(T));
+            }
+#endif
+            if (Unsafe.SizeOf<T>() > (uint)source.Length)
+            {
+                value = default;
+                return false;
+            }
+            value = Unsafe.ReadUnaligned<T>(ref GetReference(source));
+            return true;
+        }
+
+        /// <summary>
+        /// Writes a structure of type T into a span of bytes.
+        /// </summary>
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public static void WriteMachineEndian<T>(Span<byte> destination, ref T value)
+            where T : struct
+        {
+#if netstandard
+            if (SpanHelpers.IsReferenceOrContainsReferences<T>())
+            {
+                ThrowHelper.ThrowArgumentException_InvalidTypeWithPointersNotSupported(typeof(T));
+            }
+#else
+            if (RuntimeHelpers.IsReferenceOrContainsReferences<T>())
+            {
+                ThrowHelper.ThrowInvalidTypeWithPointersNotSupported(typeof(T));
+            }
+#endif
+            if ((uint)Unsafe.SizeOf<T>() > (uint)destination.Length)
+            {
+                ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.length);
+            }
+            Unsafe.WriteUnaligned<T>(ref GetReference(destination), value);
+        }
+
+        /// <summary>
+        /// Writes a structure of type T into a span of bytes.
+        /// <returns>If the span is too small to contain the type T, return false.</returns>
+        /// </summary>
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public static bool TryWriteMachineEndian<T>(Span<byte> destination, ref T value)
+            where T : struct
+        {
+#if netstandard
+            if (SpanHelpers.IsReferenceOrContainsReferences<T>())
+            {
+                ThrowHelper.ThrowArgumentException_InvalidTypeWithPointersNotSupported(typeof(T));
+            }
+#else
+            if (RuntimeHelpers.IsReferenceOrContainsReferences<T>())
+            {
+                ThrowHelper.ThrowInvalidTypeWithPointersNotSupported(typeof(T));
+            }
+#endif
+            if (Unsafe.SizeOf<T>() > (uint)destination.Length)
+            {
+                return false;
+            }
+            Unsafe.WriteUnaligned<T>(ref GetReference(destination), value);
+            return true;
+        }
     }
 }