Add more span-based Vector<T> ctors and CopyTo methods (#23333)
authorLevi Broderick <GrabYourPitchforks@users.noreply.github.com>
Tue, 30 Apr 2019 04:37:58 +0000 (21:37 -0700)
committerGitHub <noreply@github.com>
Tue, 30 Apr 2019 04:37:58 +0000 (21:37 -0700)
src/System.Private.CoreLib/shared/System/Numerics/Vector.cs
src/System.Private.CoreLib/shared/System/Numerics/Vector.tt
src/System.Private.CoreLib/shared/System/ThrowHelper.cs

index 3f7832a..7d8937d 100644 (file)
@@ -59,10 +59,10 @@ namespace System.Numerics
             [Intrinsic]
             get
             {
-                return s_count;
+                ThrowHelper.ThrowForUnsupportedVectorBaseType<T>();
+                return Unsafe.SizeOf<Vector<T>>() / Unsafe.SizeOf<T>();
             }
         }
-        private static readonly int s_count = InitializeCount();
 
         /// <summary>
         /// Returns a vector containing all zeroes.
@@ -101,71 +101,6 @@ namespace System.Numerics
         private static readonly Vector<T> s_allOnes = new Vector<T>(GetAllBitsSetValue());
         #endregion Static Members
 
-        #region Static Initialization
-        private struct VectorSizeHelper
-        {
-            internal Vector<T> _placeholder;
-            internal byte _byte;
-        }
-
-        // Calculates the size of this struct in bytes, by computing the offset of a field in a structure
-        private static unsafe int InitializeCount()
-        {
-            VectorSizeHelper vsh;
-            byte* vectorBase = &vsh._placeholder.register.byte_0;
-            byte* byteBase = &vsh._byte;
-            int vectorSizeInBytes = (int)(byteBase - vectorBase);
-
-            int typeSizeInBytes = -1;
-            if (typeof(T) == typeof(byte))
-            {
-                typeSizeInBytes = sizeof(byte);
-            }
-            else if (typeof(T) == typeof(sbyte))
-            {
-                typeSizeInBytes = sizeof(sbyte);
-            }
-            else if (typeof(T) == typeof(ushort))
-            {
-                typeSizeInBytes = sizeof(ushort);
-            }
-            else if (typeof(T) == typeof(short))
-            {
-                typeSizeInBytes = sizeof(short);
-            }
-            else if (typeof(T) == typeof(uint))
-            {
-                typeSizeInBytes = sizeof(uint);
-            }
-            else if (typeof(T) == typeof(int))
-            {
-                typeSizeInBytes = sizeof(int);
-            }
-            else if (typeof(T) == typeof(ulong))
-            {
-                typeSizeInBytes = sizeof(ulong);
-            }
-            else if (typeof(T) == typeof(long))
-            {
-                typeSizeInBytes = sizeof(long);
-            }
-            else if (typeof(T) == typeof(float))
-            {
-                typeSizeInBytes = sizeof(float);
-            }
-            else if (typeof(T) == typeof(double))
-            {
-                typeSizeInBytes = sizeof(double);
-            }
-            else
-            {
-                throw new NotSupportedException(SR.Arg_TypeNotSupported);
-            }
-
-            return vectorSizeInBytes / typeSizeInBytes;
-        }
-        #endregion Static Initialization
-
         #region Constructors
         /// <summary>
         /// Constructs a vector whose components are all <code>value</code>
@@ -779,38 +714,82 @@ namespace System.Numerics
 
 #if netcoreapp
         /// <summary>
-        /// Constructs a vector from the given span. The span must contain at least Vector'T.Count elements.
+        /// Constructs a vector from the given <see cref="ReadOnlySpan{Byte}"/>. The span must contain at least <see cref="Vector{Byte}.Count"/> elements.
         /// </summary>
-        public Vector(Span<T> values)
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public Vector(ReadOnlySpan<byte> values)
             : this()
         {
-            if ((typeof(T) == typeof(byte))
-                || (typeof(T) == typeof(sbyte))
-                || (typeof(T) == typeof(ushort))
-                || (typeof(T) == typeof(short))
-                || (typeof(T) == typeof(uint))
-                || (typeof(T) == typeof(int))
-                || (typeof(T) == typeof(ulong))
-                || (typeof(T) == typeof(long))
-                || (typeof(T) == typeof(float))
-                || (typeof(T) == typeof(double)))
+            ThrowHelper.ThrowForUnsupportedVectorBaseType<T>();
+            if (values.Length < Vector<byte>.Count)
             {
-                if (values.Length < Count)
-                {
-                    throw new IndexOutOfRangeException(SR.Format(SR.Arg_InsufficientNumberOfElements, Vector<T>.Count, nameof(values)));
-                }
-                this = Unsafe.ReadUnaligned<Vector<T>>(ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(values)));
+                Vector.ThrowInsufficientNumberOfElementsException(Vector<byte>.Count);
             }
-            else
+            this = Unsafe.ReadUnaligned<Vector<T>>(ref MemoryMarshal.GetReference(values));
+        }
+        
+        /// <summary>
+        /// Constructs a vector from the given <see cref="ReadOnlySpan{T}"/>. The span must contain at least <see cref="Vector{T}.Count"/> elements.
+        /// </summary>
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public Vector(ReadOnlySpan<T> values)
+            : this()
+        {
+            if (values.Length < Count)
             {
-                throw new NotSupportedException(SR.Arg_TypeNotSupported);
+                Vector.ThrowInsufficientNumberOfElementsException(Vector<T>.Count);
             }
+            this = Unsafe.ReadUnaligned<Vector<T>>(ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(values)));
+        }
+
+        /// <summary>
+        /// Constructs a vector from the given <see cref="Span{T}"/>. The span must contain at least <see cref="Vector{T}.Count"/> elements.
+        /// </summary>
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public Vector(Span<T> values)
+            : this()
+        {
+            if (values.Length < Count)
+            {
+                Vector.ThrowInsufficientNumberOfElementsException(Vector<T>.Count);
+            }
+            this = Unsafe.ReadUnaligned<Vector<T>>(ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(values)));
         }
 #endif
         #endregion Constructors
 
         #region Public Instance Methods
         /// <summary>
+        /// Copies the vector to the given <see cref="Span{Byte}"/>. The destination span must be at least size <see cref="Vector{Byte}.Count"/>.
+        /// </summary>
+        /// <param name="destination">The destination span which the values are copied into</param>
+        /// <exception cref="ArgumentException">If number of elements in source vector is greater than those available in destination span</exception>
+        public void CopyTo(Span<byte> destination)
+        {
+            ThrowHelper.ThrowForUnsupportedVectorBaseType<T>();
+            if ((uint)destination.Length < (uint)Vector<byte>.Count)
+            {
+                ThrowHelper.ThrowArgumentException_DestinationTooShort();
+            }
+            Unsafe.WriteUnaligned<Vector<T>>(ref MemoryMarshal.GetReference(destination), this);
+        }
+
+        /// <summary>
+        /// Copies the vector to the given <see cref="Span{T}"/>. The destination span must be at least size <see cref="Vector{T}.Count"/>.
+        /// </summary>
+        /// <param name="destination">The destination span which the values are copied into</param>
+        /// <exception cref="ArgumentException">If number of elements in source vector is greater than those available in destination span</exception>
+        public void CopyTo(Span<T> destination)
+        {
+            if ((uint)destination.Length < (uint)Count)
+            {
+                ThrowHelper.ThrowArgumentException_DestinationTooShort();
+            }
+
+            Unsafe.WriteUnaligned<Vector<T>>(ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(destination)), this);
+        }
+
+        /// <summary>
         /// Copies the vector to the given destination array. The destination array must be at least size Vector'T.Count.
         /// </summary>
         /// <param name="destination">The destination array which the values are copied into</param>
@@ -1591,6 +1570,41 @@ namespace System.Numerics
             sb.Append('>');
             return sb.ToString();
         }
+
+        /// <summary>
+        /// Attempts to copy the vector to the given <see cref="Span{Byte}"/>. The destination span must be at least size <see cref="Vector{Byte}.Count"/>.
+        /// </summary>
+        /// <param name="destination">The destination span which the values are copied into</param>
+        /// <returns>True if the source vector was successfully copied to <paramref name="destination"/>. False if
+        /// <paramref name="destination"/> is not large enough to hold the source vector.</returns>
+        public bool TryCopyTo(Span<byte> destination)
+        {
+            ThrowHelper.ThrowForUnsupportedVectorBaseType<T>();
+            if ((uint)destination.Length < (uint)Vector<byte>.Count)
+            {
+                return false;
+            }
+
+            Unsafe.WriteUnaligned<Vector<T>>(ref MemoryMarshal.GetReference(destination), this);
+            return true;
+        }
+
+        /// <summary>
+        /// Attempts to copy the vector to the given <see cref="Span{T}"/>. The destination span must be at least size <see cref="Vector{T}.Count"/>.
+        /// </summary>
+        /// <param name="destination">The destination span which the values are copied into</param>
+        /// <returns>True if the source vector was successfully copied to <paramref name="destination"/>. False if
+        /// <paramref name="destination"/> is not large enough to hold the source vector.</returns>
+        public bool TryCopyTo(Span<T> destination)
+        {
+            if ((uint)destination.Length < (uint)Count)
+            {
+                return false;
+            }
+
+            Unsafe.WriteUnaligned<Vector<T>>(ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(destination)), this);
+            return true;
+        }
         #endregion Public Instance Methods
 
         #region Arithmetic Operators
@@ -5335,5 +5349,12 @@ namespace System.Numerics
         }
 
         #endregion Same-Size Conversion
+
+        #region Throw Helpers
+        internal static void ThrowInsufficientNumberOfElementsException(int requiredElementCount)
+        {
+            throw new IndexOutOfRangeException(SR.Format(SR.Arg_InsufficientNumberOfElements, requiredElementCount, "values"));
+        }
+        #endregion
     }
 }
index 5132b09..4eced47 100644 (file)
@@ -64,10 +64,10 @@ namespace System.Numerics
             [Intrinsic]
             get
             {
-                return s_count;
+                ThrowHelper.ThrowForUnsupportedVectorBaseType<T>();
+                return Unsafe.SizeOf<Vector<T>>() / Unsafe.SizeOf<T>();
             }
         }
-        private static readonly int s_count = InitializeCount();
 
         /// <summary>
         /// Returns a vector containing all zeroes.
@@ -106,42 +106,6 @@ namespace System.Numerics
         private static readonly Vector<T> s_allOnes = new Vector<T>(GetAllBitsSetValue());
         #endregion Static Members
 
-        #region Static Initialization
-        private struct VectorSizeHelper
-        {
-            internal Vector<T> _placeholder;
-            internal byte _byte;
-        }
-
-        // Calculates the size of this struct in bytes, by computing the offset of a field in a structure
-        private static unsafe int InitializeCount()
-        {
-            VectorSizeHelper vsh;
-            byte* vectorBase = &vsh._placeholder.register.byte_0;
-            byte* byteBase = &vsh._byte;
-            int vectorSizeInBytes = (int)(byteBase - vectorBase);
-
-            int typeSizeInBytes = -1;
-<#
-    foreach (Type type in supportedTypes)
-    {
-#>
-            <#=GenerateIfStatementHeader(type)#>
-            {
-                typeSizeInBytes = sizeof(<#=typeAliases[type]#>);
-            }
-<#
-    }
-#>
-            else
-            {
-                throw new NotSupportedException(SR.Arg_TypeNotSupported);
-            }
-
-            return vectorSizeInBytes / typeSizeInBytes;
-        }
-        #endregion Static Initialization
-
         #region Constructors
         /// <summary>
         /// Constructs a vector whose components are all <code>value</code>
@@ -305,29 +269,82 @@ namespace System.Numerics
 
 #if netcoreapp
         /// <summary>
-        /// Constructs a vector from the given span. The span must contain at least Vector'T.Count elements.
+        /// Constructs a vector from the given <see cref="ReadOnlySpan{Byte}"/>. The span must contain at least <see cref="Vector{Byte}.Count"/> elements.
         /// </summary>
-        public Vector(Span<T> values)
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public Vector(ReadOnlySpan<byte> values)
             : this()
         {
-            <#=GenerateIfConditionAllTypes(supportedTypes)#>
+            ThrowHelper.ThrowForUnsupportedVectorBaseType<T>();
+            if (values.Length < Vector<byte>.Count)
             {
-                if (values.Length < Count)
-                {
-                    throw new IndexOutOfRangeException(SR.Format(SR.Arg_InsufficientNumberOfElements, Vector<T>.Count, nameof(values)));
-                }
-                this = Unsafe.ReadUnaligned<Vector<T>>(ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(values)));
+                Vector.ThrowInsufficientNumberOfElementsException(Vector<byte>.Count);
             }
-            else
+            this = Unsafe.ReadUnaligned<Vector<T>>(ref MemoryMarshal.GetReference(values));
+        }
+        
+        /// <summary>
+        /// Constructs a vector from the given <see cref="ReadOnlySpan{T}"/>. The span must contain at least <see cref="Vector{T}.Count"/> elements.
+        /// </summary>
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public Vector(ReadOnlySpan<T> values)
+            : this()
+        {
+            if (values.Length < Count)
             {
-                throw new NotSupportedException(SR.Arg_TypeNotSupported);
+                Vector.ThrowInsufficientNumberOfElementsException(Vector<T>.Count);
+            }
+            this = Unsafe.ReadUnaligned<Vector<T>>(ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(values)));
+        }
+
+        /// <summary>
+        /// Constructs a vector from the given <see cref="Span{T}"/>. The span must contain at least <see cref="Vector{T}.Count"/> elements.
+        /// </summary>
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public Vector(Span<T> values)
+            : this()
+        {
+            if (values.Length < Count)
+            {
+                Vector.ThrowInsufficientNumberOfElementsException(Vector<T>.Count);
             }
+            this = Unsafe.ReadUnaligned<Vector<T>>(ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(values)));
         }
 #endif
         #endregion Constructors
 
         #region Public Instance Methods
         /// <summary>
+        /// Copies the vector to the given <see cref="Span{Byte}"/>. The destination span must be at least size <see cref="Vector{Byte}.Count"/>.
+        /// </summary>
+        /// <param name="destination">The destination span which the values are copied into</param>
+        /// <exception cref="ArgumentException">If number of elements in source vector is greater than those available in destination span</exception>
+        public void CopyTo(Span<byte> destination)
+        {
+            ThrowHelper.ThrowForUnsupportedVectorBaseType<T>();
+            if ((uint)destination.Length < (uint)Vector<byte>.Count)
+            {
+                ThrowHelper.ThrowArgumentException_DestinationTooShort();
+            }
+            Unsafe.WriteUnaligned<Vector<T>>(ref MemoryMarshal.GetReference(destination), this);
+        }
+
+        /// <summary>
+        /// Copies the vector to the given <see cref="Span{T}"/>. The destination span must be at least size <see cref="Vector{T}.Count"/>.
+        /// </summary>
+        /// <param name="destination">The destination span which the values are copied into</param>
+        /// <exception cref="ArgumentException">If number of elements in source vector is greater than those available in destination span</exception>
+        public void CopyTo(Span<T> destination)
+        {
+            if ((uint)destination.Length < (uint)Count)
+            {
+                ThrowHelper.ThrowArgumentException_DestinationTooShort();
+            }
+
+            Unsafe.WriteUnaligned<Vector<T>>(ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(destination)), this);
+        }
+
+        /// <summary>
         /// Copies the vector to the given destination array. The destination array must be at least size Vector'T.Count.
         /// </summary>
         /// <param name="destination">The destination array which the values are copied into</param>
@@ -620,6 +637,41 @@ namespace System.Numerics
             sb.Append('>');
             return sb.ToString();
         }
+
+        /// <summary>
+        /// Attempts to copy the vector to the given <see cref="Span{Byte}"/>. The destination span must be at least size <see cref="Vector{Byte}.Count"/>.
+        /// </summary>
+        /// <param name="destination">The destination span which the values are copied into</param>
+        /// <returns>True if the source vector was successfully copied to <paramref name="destination"/>. False if
+        /// <paramref name="destination"/> is not large enough to hold the source vector.</returns>
+        public bool TryCopyTo(Span<byte> destination)
+        {
+            ThrowHelper.ThrowForUnsupportedVectorBaseType<T>();
+            if ((uint)destination.Length < (uint)Vector<byte>.Count)
+            {
+                return false;
+            }
+
+            Unsafe.WriteUnaligned<Vector<T>>(ref MemoryMarshal.GetReference(destination), this);
+            return true;
+        }
+
+        /// <summary>
+        /// Attempts to copy the vector to the given <see cref="Span{T}"/>. The destination span must be at least size <see cref="Vector{T}.Count"/>.
+        /// </summary>
+        /// <param name="destination">The destination span which the values are copied into</param>
+        /// <returns>True if the source vector was successfully copied to <paramref name="destination"/>. False if
+        /// <paramref name="destination"/> is not large enough to hold the source vector.</returns>
+        public bool TryCopyTo(Span<T> destination)
+        {
+            if ((uint)destination.Length < (uint)Count)
+            {
+                return false;
+            }
+
+            Unsafe.WriteUnaligned<Vector<T>>(ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(destination)), this);
+            return true;
+        }
         #endregion Public Instance Methods
 
         #region Arithmetic Operators
@@ -1836,5 +1888,12 @@ namespace System.Numerics
     }
 #>
         #endregion Same-Size Conversion
+
+        #region Throw Helpers
+        internal static void ThrowInsufficientNumberOfElementsException(int requiredElementCount)
+        {
+            throw new IndexOutOfRangeException(SR.Format(SR.Arg_InsufficientNumberOfElements, requiredElementCount, "values"));
+        }
+        #endregion
     }
 }
index e73e1fa..8b46097 100644 (file)
@@ -404,6 +404,9 @@ namespace System
                 ThrowHelper.ThrowArgumentNullException(argName);
         }
 
+        // Throws if 'T' is disallowed in Vector<T> / Vector128<T> / other related types in the
+        // Numerics or Intrinsics namespaces. If 'T' is allowed, no-ops. JIT will elide the method
+        // entirely if 'T' is supported and we're on an optimized release build.
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         internal static void ThrowForUnsupportedVectorBaseType<T>() where T : struct
         {