[netcore] Optimize Buffer.BlockCopy and Buffer.IsPrimitiveArray (mono/mono#16692)
authorEgor Bogatov <egorbo@gmail.com>
Mon, 7 Oct 2019 10:29:22 +0000 (13:29 +0300)
committerMarek Safar <marek.safar@gmail.com>
Mon, 7 Oct 2019 10:29:22 +0000 (12:29 +0200)
* Optimize Buffer.BlockCopy and IsPrimitiveArray (make them icalls)

* wrap with ENABLE_NETCORE

* fix formatting

* fix check for "is primitive"

* use m_class_is_primitive

* fix build errors

* fix typo

* fix failing test

* use mono_error_set_argument_out_of_range

* fix argument names for exceptions

Commit migrated from https://github.com/mono/mono/commit/35c6b7691f4043fd49d8393b84224f0d1df1e7fe

src/mono/mono/metadata/icall-def-netcore.h
src/mono/mono/metadata/icall.c
src/mono/netcore/System.Private.CoreLib/src/System/Buffer.cs

index eeb5a4d..bfd0c17 100644 (file)
@@ -57,7 +57,9 @@ HANDLES(ARRAY_12, "SetValue",         ves_icall_System_Array_SetValue, void, 3,
 HANDLES(ARRAY_13, "SetValueImpl",  ves_icall_System_Array_SetValueImpl, void, 3, (MonoArray, MonoObject, guint32))
 HANDLES(ARRAY_14, "SetValueRelaxedImpl",  ves_icall_System_Array_SetValueRelaxedImpl, void, 3, (MonoArray, MonoObject, guint32))
 
-ICALL_TYPE(BUFFER, "System.Buffer", BUFFER_1)
+ICALL_TYPE(BUFFER, "System.Buffer", BUFFER_0)
+HANDLES(BUFFER_0, "BlockCopy", ves_icall_System_Buffer_BlockCopy, void, 5, (MonoArray, int, MonoArray, int, int))
+HANDLES(BUFFER_0a, "IsPrimitiveTypeArray", ves_icall_System_Buffer_IsPrimitiveTypeArray, MonoBoolean, 1, (MonoArray))
 HANDLES(BUFFER_1, "_ByteLength", ves_icall_System_Buffer_ByteLengthInternal, gint32, 1, (MonoArray))
 NOHANDLES(ICALL(BUFFER_2, "__Memmove", ves_icall_System_Runtime_RuntimeImports_Memmove))
 
index 391936c..74ecfe2 100644 (file)
@@ -7201,6 +7201,66 @@ mono_array_get_byte_length (MonoArrayHandle array)
        }
 }
 
+#ifdef ENABLE_NETCORE
+void
+ves_icall_System_Buffer_BlockCopy (MonoArrayHandle src, int src_offset, MonoArrayHandle dst, int dst_offset, int count, MonoError *error)
+{
+       MONO_CHECK_ARG_NULL_HANDLE (src,);
+       MONO_CHECK_ARG_NULL_HANDLE (dst,);
+
+       if (src_offset < 0) {
+               mono_error_set_argument_out_of_range (error, "srcOffset", "Value must be non-negative and less than or equal to Int32.MaxValue.");
+               return;
+       }
+
+       if (dst_offset < 0) {
+               mono_error_set_argument_out_of_range (error, "dstOffset", "Value must be non-negative and less than or equal to Int32.MaxValue.");
+               return;
+       }
+
+       if (count < 0) {
+               mono_error_set_argument_out_of_range (error, "count", "Value must be non-negative and less than or equal to Int32.MaxValue.");
+               return;
+       }
+
+       MonoClass * const src_class = m_class_get_element_class (MONO_HANDLE_GETVAL (src, obj.vtable)->klass);
+       MonoClass * const dst_class = m_class_get_element_class (MONO_HANDLE_GETVAL (dst, obj.vtable)->klass);
+
+       if (!m_class_is_primitive (src_class)) {
+               mono_error_set_argument (error, "src", "Object must be an array of primitives.");
+               return;
+       }
+
+       if (!m_class_is_primitive (dst_class)) {
+               mono_error_set_argument (error, "dst", "Object must be an array of primitives.");
+               return;
+       }
+
+       if ((src_offset > mono_array_get_byte_length (src) - count) || (dst_offset > mono_array_get_byte_length (dst) - count)) {
+               mono_error_set_argument (error, "", "Offset and length were out of bounds for the array or count is greater than the number of elements from index to the end of the source collection.");
+               return;
+       }
+
+       MONO_ENTER_NO_SAFEPOINTS;
+
+       guint8 const * const src_buf = (guint8*)MONO_HANDLE_RAW (src)->vector + src_offset;
+       guint8* const dst_buf = (guint8*)MONO_HANDLE_RAW (dst)->vector + dst_offset;
+
+       memmove (dst_buf, src_buf, count);
+
+       MONO_EXIT_NO_SAFEPOINTS;
+
+       return;
+}
+
+MonoBoolean
+ves_icall_System_Buffer_IsPrimitiveTypeArray (MonoArrayHandle array, MonoError* error)
+{
+       MonoClass * const klass = m_class_get_element_class (MONO_HANDLE_GETVAL (array, obj.vtable)->klass);
+       return m_class_is_primitive (klass);
+}
+#endif
+
 gint32
 ves_icall_System_Buffer_ByteLengthInternal (MonoArrayHandle array, MonoError* error)
 {
index de5ec80..0fe168d 100644 (file)
@@ -2,9 +2,7 @@
 // The .NET Foundation licenses this file to you under the MIT license.
 // See the LICENSE file in the project root for more information.
 
-using System.Runtime;
 using System.Runtime.CompilerServices;
-using Internal.Runtime.CompilerServices;
 
 #if BIT64
 using nuint = System.UInt64;
@@ -16,53 +14,14 @@ namespace System
 {
        partial class Buffer
        {
-               public static void BlockCopy (Array src, int srcOffset, Array dst, int dstOffset, int count)
-               {
-                       if (src == null)
-                               throw new ArgumentNullException (nameof (src));
-                       if (dst == null)
-                               throw new ArgumentNullException ("dst");
-
-                       if (srcOffset < 0)
-                               throw new ArgumentOutOfRangeException (nameof (srcOffset), SR.ArgumentOutOfRange_MustBeNonNegInt32);
-                       if (dstOffset < 0)
-                               throw new ArgumentOutOfRangeException (nameof (dstOffset), SR.ArgumentOutOfRange_MustBeNonNegInt32);
-                       if (count < 0)
-                               throw new ArgumentOutOfRangeException (nameof (count), SR.ArgumentOutOfRange_MustBeNonNegInt32);
-                       if (!IsPrimitiveTypeArray (src))
-                               throw new ArgumentException (SR.Arg_MustBePrimArray, nameof (src));
-                       if (!IsPrimitiveTypeArray (dst))
-                               throw new ArgumentException (SR.Arg_MustBePrimArray, nameof (dst));
-
-                       var uCount = (nuint) count;
-                       var uSrcOffset = (nuint) srcOffset;
-                       var uDstOffset = (nuint) dstOffset;
-
-                       var uSrcLen = (nuint) ByteLength (src);
-                       var uDstLen = (nuint) ByteLength (dst);
-
-                       if (uSrcLen < uSrcOffset + uCount)
-                               throw new ArgumentException (SR.Argument_InvalidOffLen, "");
-                       if (uDstLen < uDstOffset + uCount)
-                               throw new ArgumentException (SR.Argument_InvalidOffLen, "");
-
-                       if (uCount != 0) {
-                               unsafe {
-                                       fixed (byte* pSrc = &src.GetRawArrayData (), pDst = &dst.GetRawArrayData ()) {
-                                               Memmove (pDst + uDstOffset, pSrc + uSrcOffset, uCount);
-                                       }
-                               }
-                       }
-               }
+               [MethodImpl (MethodImplOptions.InternalCall)]
+               public static extern void BlockCopy (Array src, int srcOffset, Array dst, int dstOffset, int count);
 
-               [MethodImplAttribute (MethodImplOptions.InternalCall)]
+               [MethodImpl (MethodImplOptions.InternalCall)]
                static extern int _ByteLength (Array array);
 
-               static bool IsPrimitiveTypeArray (Array array)
-               {
-                       // TODO: optimize                       
-                       return array.GetType ().GetElementType ()!.IsPrimitive;
-               }
+               [MethodImpl (MethodImplOptions.InternalCall)]
+               static extern bool IsPrimitiveTypeArray (Array array);
 
                internal static unsafe void Memcpy (byte* dest, byte* src, int len) => Memmove (dest, src, (nuint) len);