+++ /dev/null
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-//
-// File: ArrayHelpers.cpp
-//
-
-//
-
-#include "common.h"
-
-#include <object.h>
-#include "ceeload.h"
-
-#include "excep.h"
-#include "frames.h"
-#include "vars.hpp"
-#include "classnames.h"
-#include "arrayhelpers.h"
-#include <memory.h>
-
-INT32 ArrayHelper::IndexOfUINT8( UINT8* array, UINT32 index, UINT32 count, UINT8 value) {
- LIMITED_METHOD_CONTRACT;
- UINT8 * pvalue = (UINT8 *)memchr(array + index, value, count);
- if ( NULL == pvalue ) {
- return -1;
- }
- else {
- return static_cast<INT32>(pvalue - array);
- }
-}
-
-
-// A fast IndexOf method for arrays of primitive types. Returns TRUE or FALSE
-// if it succeeds, and stores result in retVal.
-FCIMPL5(FC_BOOL_RET, ArrayHelper::TrySZIndexOf, ArrayBase * array, UINT32 index, UINT32 count, Object * value, INT32 * retVal)
- FCALL_CONTRACT;
-
- VALIDATEOBJECT(array);
- _ASSERTE(array != NULL);
-
- // <TODO>@TODO: Eventually, consider adding support for single dimension arrays with
- // non-zero lower bounds. VB might care. </TODO>
- if (array->GetRank() != 1 || array->GetLowerBoundsPtr()[0] != 0)
- FC_RETURN_BOOL(FALSE);
-
- _ASSERTE(retVal != NULL);
- _ASSERTE(index <= array->GetNumComponents());
- _ASSERTE(count <= array->GetNumComponents());
- _ASSERTE(array->GetNumComponents() >= index + count);
- *retVal = 0xdeadbeef; // Initialize the return value.
- // value can be NULL, but of course, will not be in primitive arrays.
-
- TypeHandle arrayTH = array->GetArrayElementTypeHandle();
- const CorElementType arrayElType = arrayTH.GetVerifierCorElementType();
- if (!CorTypeInfo::IsPrimitiveType_NoThrow(arrayElType))
- FC_RETURN_BOOL(FALSE);
- // Handle special case of looking for a NULL object in a primitive array.
- if (value == NULL) {
- *retVal = -1;
- FC_RETURN_BOOL(TRUE);
- }
- TypeHandle valueTH = value->GetTypeHandle();
- if (arrayTH != valueTH)
- FC_RETURN_BOOL(FALSE);
-
-
- switch(arrayElType) {
- case ELEMENT_TYPE_I1:
- case ELEMENT_TYPE_U1:
- case ELEMENT_TYPE_BOOLEAN:
- *retVal = IndexOfUINT8((U1*) array->GetDataPtr(), index, count, *(U1*)value->UnBox());
- break;
-
- case ELEMENT_TYPE_I2:
- case ELEMENT_TYPE_U2:
- case ELEMENT_TYPE_CHAR:
- *retVal = ArrayHelpers<U2>::IndexOf((U2*) array->GetDataPtr(), index, count, *(U2*)value->UnBox());
- break;
-
- case ELEMENT_TYPE_I4:
- case ELEMENT_TYPE_U4:
- case ELEMENT_TYPE_R4:
- IN_TARGET_32BIT(case ELEMENT_TYPE_I:)
- IN_TARGET_32BIT(case ELEMENT_TYPE_U:)
- *retVal = ArrayHelpers<U4>::IndexOf((U4*) array->GetDataPtr(), index, count, *(U4*)value->UnBox());
- break;
-
- case ELEMENT_TYPE_I8:
- case ELEMENT_TYPE_U8:
- case ELEMENT_TYPE_R8:
- IN_TARGET_64BIT(case ELEMENT_TYPE_I:)
- IN_TARGET_64BIT(case ELEMENT_TYPE_U:)
- *retVal = ArrayHelpers<U8>::IndexOf((U8*) array->GetDataPtr(), index, count, *(U8*)value->UnBox());
- break;
-
- default:
- _ASSERTE(!"Unrecognized primitive type in ArrayHelper::TrySZIndexOf");
- FC_RETURN_BOOL(FALSE);
- }
- FC_RETURN_BOOL(TRUE);
-FCIMPLEND
-
-// A fast LastIndexOf method for arrays of primitive types. Returns TRUE or FALSE
-// if it succeeds, and stores result in retVal.
-FCIMPL5(FC_BOOL_RET, ArrayHelper::TrySZLastIndexOf, ArrayBase * array, UINT32 index, UINT32 count, Object * value, INT32 * retVal)
-{
- FCALL_CONTRACT;
-
- VALIDATEOBJECT(array);
- _ASSERTE(array != NULL);
-
- // <TODO>@TODO: Eventually, consider adding support for single dimension arrays with
- // non-zero lower bounds. VB might care. </TODO>
- if (array->GetRank() != 1 || array->GetLowerBoundsPtr()[0] != 0)
- FC_RETURN_BOOL(FALSE);
-
- _ASSERTE(retVal != NULL);
- *retVal = 0xdeadbeef; // Initialize the return value.
- // value can be NULL, but of course, will not be in primitive arrays.
-
- TypeHandle arrayTH = array->GetArrayElementTypeHandle();
- const CorElementType arrayElType = arrayTH.GetVerifierCorElementType();
- if (!CorTypeInfo::IsPrimitiveType_NoThrow(arrayElType))
- FC_RETURN_BOOL(FALSE);
- // Handle special case of looking for a NULL object in a primitive array.
- // Also handle case where the array is of 0 length.
- if (value == NULL || array->GetNumComponents() == 0) {
- *retVal = -1;
- FC_RETURN_BOOL(TRUE);
- }
-
- _ASSERTE(index < array->GetNumComponents());
- _ASSERTE(count <= array->GetNumComponents());
- _ASSERTE(index + 1 >= count);
-
- TypeHandle valueTH = value->GetTypeHandle();
- if (arrayTH != valueTH)
- FC_RETURN_BOOL(FALSE);
-
-
- switch(arrayElType) {
- case ELEMENT_TYPE_I1:
- case ELEMENT_TYPE_U1:
- case ELEMENT_TYPE_BOOLEAN:
- *retVal = ArrayHelpers<U1>::LastIndexOf((U1*) array->GetDataPtr(), index, count, *(U1*)value->UnBox());
- break;
-
- case ELEMENT_TYPE_I2:
- case ELEMENT_TYPE_U2:
- case ELEMENT_TYPE_CHAR:
- *retVal = ArrayHelpers<U2>::LastIndexOf((U2*) array->GetDataPtr(), index, count, *(U2*)value->UnBox());
- break;
-
- case ELEMENT_TYPE_I4:
- case ELEMENT_TYPE_U4:
- case ELEMENT_TYPE_R4:
- IN_TARGET_32BIT(case ELEMENT_TYPE_I:)
- IN_TARGET_32BIT(case ELEMENT_TYPE_U:)
- *retVal = ArrayHelpers<U4>::LastIndexOf((U4*) array->GetDataPtr(), index, count, *(U4*)value->UnBox());
- break;
-
- case ELEMENT_TYPE_I8:
- case ELEMENT_TYPE_U8:
- case ELEMENT_TYPE_R8:
- IN_TARGET_64BIT(case ELEMENT_TYPE_I:)
- IN_TARGET_64BIT(case ELEMENT_TYPE_U:)
- *retVal = ArrayHelpers<U8>::LastIndexOf((U8*) array->GetDataPtr(), index, count, *(U8*)value->UnBox());
- break;
-
- default:
- _ASSERTE(!"Unrecognized primitive type in ArrayHelper::TrySZLastIndexOf");
- FC_RETURN_BOOL(FALSE);
- }
- FC_RETURN_BOOL(TRUE);
-}
-FCIMPLEND
-
-
-FCIMPL5(FC_BOOL_RET, ArrayHelper::TrySZBinarySearch, ArrayBase * array, UINT32 index, UINT32 count, Object * value, INT32 * retVal)
- FCALL_CONTRACT;
-
- VALIDATEOBJECT(array);
- _ASSERTE(array != NULL);
-
- // <TODO>@TODO: Eventually, consider adding support for single dimension arrays with
- // non-zero lower bounds. VB might care. </TODO>
- if (array->GetRank() != 1 || array->GetLowerBoundsPtr()[0] != 0)
- FC_RETURN_BOOL(FALSE);
-
- _ASSERTE(retVal != NULL);
- _ASSERTE(index <= array->GetNumComponents());
- _ASSERTE(count <= array->GetNumComponents());
- _ASSERTE(array->GetNumComponents() >= index + count);
- *retVal = 0xdeadbeef; // Initialize the return value.
- // value can be NULL, but of course, will not be in primitive arrays.
- TypeHandle arrayTH = array->GetArrayElementTypeHandle();
- const CorElementType arrayElType = arrayTH.GetVerifierCorElementType();
- if (!CorTypeInfo::IsPrimitiveType_NoThrow(arrayElType))
- FC_RETURN_BOOL(FALSE);
- // Handle special case of looking for a NULL object in a primitive array.
- if (value == NULL) {
- *retVal = -1;
- FC_RETURN_BOOL(TRUE);
- }
-
- TypeHandle valueTH = value->GetTypeHandle();
- if (arrayTH != valueTH)
- FC_RETURN_BOOL(FALSE);
-
- switch(arrayElType) {
- case ELEMENT_TYPE_I1:
- *retVal = ArrayHelpers<I1>::BinarySearchBitwiseEquals((I1*) array->GetDataPtr(), index, count, *(I1*)value->UnBox());
- break;
-
- case ELEMENT_TYPE_U1:
- case ELEMENT_TYPE_BOOLEAN:
- *retVal = ArrayHelpers<U1>::BinarySearchBitwiseEquals((U1*) array->GetDataPtr(), index, count, *(U1*)value->UnBox());
- break;
-
- case ELEMENT_TYPE_I2:
- *retVal = ArrayHelpers<I2>::BinarySearchBitwiseEquals((I2*) array->GetDataPtr(), index, count, *(I2*)value->UnBox());
- break;
-
- case ELEMENT_TYPE_U2:
- case ELEMENT_TYPE_CHAR:
- *retVal = ArrayHelpers<U2>::BinarySearchBitwiseEquals((U2*) array->GetDataPtr(), index, count, *(U2*)value->UnBox());
- break;
-
- case ELEMENT_TYPE_I4:
- *retVal = ArrayHelpers<I4>::BinarySearchBitwiseEquals((I4*) array->GetDataPtr(), index, count, *(I4*)value->UnBox());
- break;
-
- case ELEMENT_TYPE_U4:
- *retVal = ArrayHelpers<U4>::BinarySearchBitwiseEquals((U4*) array->GetDataPtr(), index, count, *(U4*)value->UnBox());
- break;
-
- case ELEMENT_TYPE_R4:
- *retVal = ArrayHelpers<R4>::BinarySearchBitwiseEquals((R4*) array->GetDataPtr(), index, count, *(R4*)value->UnBox());
- break;
-
- case ELEMENT_TYPE_I8:
- *retVal = ArrayHelpers<I8>::BinarySearchBitwiseEquals((I8*) array->GetDataPtr(), index, count, *(I8*)value->UnBox());
- break;
-
- case ELEMENT_TYPE_U8:
- *retVal = ArrayHelpers<U8>::BinarySearchBitwiseEquals((U8*) array->GetDataPtr(), index, count, *(U8*)value->UnBox());
- break;
-
- case ELEMENT_TYPE_R8:
- *retVal = ArrayHelpers<R8>::BinarySearchBitwiseEquals((R8*) array->GetDataPtr(), index, count, *(R8*)value->UnBox());
- break;
-
- case ELEMENT_TYPE_I:
- case ELEMENT_TYPE_U:
- // In V1.0, IntPtr & UIntPtr are not fully supported types. They do
- // not implement IComparable, so searching & sorting for them should
- // fail. In V1.1 or V2.0, this should change.
- FC_RETURN_BOOL(FALSE);
-
- default:
- _ASSERTE(!"Unrecognized primitive type in ArrayHelper::TrySZBinarySearch");
- FC_RETURN_BOOL(FALSE);
- }
- FC_RETURN_BOOL(TRUE);
-FCIMPLEND
-
-FCIMPL3(FC_BOOL_RET, ArrayHelper::TrySZReverse, ArrayBase * array, UINT32 index, UINT32 count)
-{
- FCALL_CONTRACT;
-
- VALIDATEOBJECT(array);
- _ASSERTE(array != NULL);
-
- // <TODO>@TODO: Eventually, consider adding support for single dimension arrays with
- // non-zero lower bounds. VB might care. </TODO>
- if (array->GetRank() != 1 || array->GetLowerBoundsPtr()[0] != 0)
- FC_RETURN_BOOL(FALSE);
-
- _ASSERTE(index <= array->GetNumComponents());
- _ASSERTE(count <= array->GetNumComponents());
- _ASSERTE(array->GetNumComponents() >= index + count);
-
- TypeHandle arrayTH = array->GetArrayElementTypeHandle();
- const CorElementType arrayElType = arrayTH.GetVerifierCorElementType();
- if (!CorTypeInfo::IsPrimitiveType_NoThrow(arrayElType))
- FC_RETURN_BOOL(FALSE);
-
- switch(arrayElType) {
- case ELEMENT_TYPE_I1:
- case ELEMENT_TYPE_U1:
- case ELEMENT_TYPE_BOOLEAN:
- ArrayHelpers<U1>::Reverse((U1*) array->GetDataPtr(), index, count);
- break;
-
- case ELEMENT_TYPE_I2:
- case ELEMENT_TYPE_U2:
- case ELEMENT_TYPE_CHAR:
- ArrayHelpers<U2>::Reverse((U2*) array->GetDataPtr(), index, count);
- break;
-
- case ELEMENT_TYPE_I4:
- case ELEMENT_TYPE_U4:
- case ELEMENT_TYPE_R4:
- IN_TARGET_32BIT(case ELEMENT_TYPE_I:)
- IN_TARGET_32BIT(case ELEMENT_TYPE_U:)
- ArrayHelpers<U4>::Reverse((U4*) array->GetDataPtr(), index, count);
- break;
-
- case ELEMENT_TYPE_I8:
- case ELEMENT_TYPE_U8:
- case ELEMENT_TYPE_R8:
- IN_TARGET_64BIT(case ELEMENT_TYPE_I:)
- IN_TARGET_64BIT(case ELEMENT_TYPE_U:)
- ArrayHelpers<U8>::Reverse((U8*) array->GetDataPtr(), index, count);
- break;
-
- default:
- _ASSERTE(!"Unrecognized primitive type in ArrayHelper::TrySZReverse");
- FC_RETURN_BOOL(FALSE);
- }
- FC_RETURN_BOOL(TRUE);
-}
-FCIMPLEND
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
+using System.Reflection;
using System.Runtime.CompilerServices;
using Internal.Runtime.CompilerServices;
ThrowHelper.ThrowRankException(ExceptionResource.Rank_MultiDimNotSupported);
comparer ??= Comparer.Default;
-#if !CORERT
- if (comparer == Comparer.Default)
- {
- int retval;
- bool r = TrySZBinarySearch(array, index, length, value, out retval);
- if (r)
- return retval;
- }
-#endif
int lo = index;
int hi = index + length - 1;
hi = i - 1;
}
}
+ return ~lo;
}
- else
+
+ if (comparer == Comparer.Default)
{
- while (lo <= hi)
+ CorElementType et = array.GetCorElementTypeOfElementType();
+ if (et.IsPrimitiveType()
+ // IntPtr/UIntPtr does not implement IComparable
+ && (et != CorElementType.ELEMENT_TYPE_I) && (et != CorElementType.ELEMENT_TYPE_U))
{
- int i = GetMedian(lo, hi);
+ if (value == null)
+ return ~index;
- int c;
- try
+ if (array.IsValueOfElementType(value))
{
- c = comparer.Compare(array.GetValue(i), value);
- }
- catch (Exception e)
- {
- ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_IComparerFailed, e);
- return default;
- }
- if (c == 0) return i;
- if (c < 0)
- {
- lo = i + 1;
- }
- else
- {
- hi = i - 1;
+ int adjustedIndex = index - lb;
+ int result = -1;
+ switch (et)
+ {
+ case CorElementType.ELEMENT_TYPE_I1:
+ result = GenericBinarySearch<sbyte>(array, adjustedIndex, length, value);
+ break;
+ case CorElementType.ELEMENT_TYPE_U1:
+ case CorElementType.ELEMENT_TYPE_BOOLEAN:
+ result = GenericBinarySearch<byte>(array, adjustedIndex, length, value);
+ break;
+ case CorElementType.ELEMENT_TYPE_I2:
+ result = GenericBinarySearch<short>(array, adjustedIndex, length, value);
+ break;
+ case CorElementType.ELEMENT_TYPE_U2:
+ case CorElementType.ELEMENT_TYPE_CHAR:
+ result = GenericBinarySearch<ushort>(array, adjustedIndex, length, value);
+ break;
+ case CorElementType.ELEMENT_TYPE_I4:
+ result = GenericBinarySearch<int>(array, adjustedIndex, length, value);
+ break;
+ case CorElementType.ELEMENT_TYPE_U4:
+ result = GenericBinarySearch<uint>(array, adjustedIndex, length, value);
+ break;
+ case CorElementType.ELEMENT_TYPE_I8:
+ result = GenericBinarySearch<long>(array, adjustedIndex, length, value);
+ break;
+ case CorElementType.ELEMENT_TYPE_U8:
+ result = GenericBinarySearch<ulong>(array, adjustedIndex, length, value);
+ break;
+ case CorElementType.ELEMENT_TYPE_R4:
+ result = GenericBinarySearch<float>(array, adjustedIndex, length, value);
+ break;
+ case CorElementType.ELEMENT_TYPE_R8:
+ result = GenericBinarySearch<double>(array, adjustedIndex, length, value);
+ break;
+ default:
+ Debug.Fail("All primitive types should be handled above");
+ break;
+ }
+
+ return (result >= 0) ? (index + result) : ~(index + ~result);
+
+ static int GenericBinarySearch<T>(Array array, int adjustedIndex, int length, object value) where T: struct, IComparable<T>
+ => UnsafeArrayAsSpan<T>(array, adjustedIndex, length).BinarySearch(Unsafe.As<byte, T>(ref value.GetRawData()));
}
}
}
+
+ while (lo <= hi)
+ {
+ int i = GetMedian(lo, hi);
+
+ int c;
+ try
+ {
+ c = comparer.Compare(array.GetValue(i), value);
+ }
+ catch (Exception e)
+ {
+ ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_IComparerFailed, e);
+ return default;
+ }
+ if (c == 0) return i;
+ if (c < 0)
+ {
+ lo = i + 1;
+ }
+ else
+ {
+ hi = i - 1;
+ }
+ }
return ~lo;
}
if (count < 0 || count > array.Length - startIndex + lb)
ThrowHelper.ThrowCountArgumentOutOfRange_ArgumentOutOfRange_Count();
-#if !CORERT
- // Try calling a quick native method to handle primitive types.
- int retVal;
- bool r = TrySZIndexOf(array, startIndex, count, value, out retVal);
- if (r)
- return retVal;
-#endif
-
int endIndex = startIndex + count;
if (array is object[] objArray)
{
return i;
}
}
+ return -1;
}
- else
+
+ CorElementType et = array.GetCorElementTypeOfElementType();
+ if (et.IsPrimitiveType())
{
- for (int i = startIndex; i < endIndex; i++)
+ if (value == null)
+ return lb - 1;
+
+ if (array.IsValueOfElementType(value))
{
- object? obj = array.GetValue(i);
- if (obj == null)
+ int adjustedIndex = startIndex - lb;
+ int result = -1;
+ switch (et)
{
- if (value == null)
- return i;
- }
- else
- {
- if (obj.Equals(value))
- return i;
+ case CorElementType.ELEMENT_TYPE_I1:
+ case CorElementType.ELEMENT_TYPE_U1:
+ case CorElementType.ELEMENT_TYPE_BOOLEAN:
+ result = GenericIndexOf<byte>(array, value, adjustedIndex, count);
+ break;
+ case CorElementType.ELEMENT_TYPE_I2:
+ case CorElementType.ELEMENT_TYPE_U2:
+ case CorElementType.ELEMENT_TYPE_CHAR:
+ result = GenericIndexOf<char>(array, value, adjustedIndex, count);
+ break;
+ case CorElementType.ELEMENT_TYPE_I4:
+ case CorElementType.ELEMENT_TYPE_U4:
+#if !BIT64
+ case CorElementType.ELEMENT_TYPE_I:
+ case CorElementType.ELEMENT_TYPE_U:
+#endif
+ result = GenericIndexOf<int>(array, value, adjustedIndex, count);
+ break;
+ case CorElementType.ELEMENT_TYPE_I8:
+ case CorElementType.ELEMENT_TYPE_U8:
+#if BIT64
+ case CorElementType.ELEMENT_TYPE_I:
+ case CorElementType.ELEMENT_TYPE_U:
+#endif
+ result = GenericIndexOf<long>(array, value, adjustedIndex, count);
+ break;
+ case CorElementType.ELEMENT_TYPE_R4:
+ result = GenericIndexOf<float>(array, value, adjustedIndex, count);
+ break;
+ case CorElementType.ELEMENT_TYPE_R8:
+ result = GenericIndexOf<double>(array, value, adjustedIndex, count);
+ break;
+ default:
+ Debug.Fail("All primitive types should be handled above");
+ break;
}
+
+ return (result >= 0 ? startIndex : lb) + result;
+
+ static int GenericIndexOf<T>(Array array, object value, int adjustedIndex, int length) where T : struct, IEquatable<T>
+ => UnsafeArrayAsSpan<T>(array, adjustedIndex, length).IndexOf(Unsafe.As<byte, T>(ref value.GetRawData()));
+ }
+ }
+
+ for (int i = startIndex; i < endIndex; i++)
+ {
+ object? obj = array.GetValue(i);
+ if (obj == null)
+ {
+ if (value == null)
+ return i;
+ }
+ else
+ {
+ if (obj.Equals(value))
+ return i;
}
}
// Return one less than the lower bound of the array. This way,
if (array.Rank != 1)
ThrowHelper.ThrowRankException(ExceptionResource.Rank_MultiDimNotSupported);
-#if !CORERT
- // Try calling a quick native method to handle primitive types.
- int retVal;
- bool r = TrySZLastIndexOf(array, startIndex, count, value, out retVal);
- if (r)
- return retVal;
-#endif
-
int endIndex = startIndex - count + 1;
if (array is object[] objArray)
{
return i;
}
}
+ return -1;
}
- else
+
+ CorElementType et = array.GetCorElementTypeOfElementType();
+ if (et.IsPrimitiveType())
{
- for (int i = startIndex; i >= endIndex; i--)
+ if (value == null)
+ return lb - 1;
+
+ if (array.IsValueOfElementType(value))
{
- object? obj = array.GetValue(i);
- if (obj == null)
+ int adjustedIndex = endIndex - lb;
+ int result = -1;
+ switch (et)
{
- if (value == null)
- return i;
- }
- else
- {
- if (obj.Equals(value))
- return i;
+ case CorElementType.ELEMENT_TYPE_I1:
+ case CorElementType.ELEMENT_TYPE_U1:
+ case CorElementType.ELEMENT_TYPE_BOOLEAN:
+ result = GenericLastIndexOf<byte>(array, value, adjustedIndex, count);
+ break;
+ case CorElementType.ELEMENT_TYPE_I2:
+ case CorElementType.ELEMENT_TYPE_U2:
+ case CorElementType.ELEMENT_TYPE_CHAR:
+ result = GenericLastIndexOf<char>(array, value, adjustedIndex, count);
+ break;
+ case CorElementType.ELEMENT_TYPE_I4:
+ case CorElementType.ELEMENT_TYPE_U4:
+#if !BIT64
+ case CorElementType.ELEMENT_TYPE_I:
+ case CorElementType.ELEMENT_TYPE_U:
+#endif
+ result = GenericLastIndexOf<int>(array, value, adjustedIndex, count);
+ break;
+ case CorElementType.ELEMENT_TYPE_I8:
+ case CorElementType.ELEMENT_TYPE_U8:
+#if BIT64
+ case CorElementType.ELEMENT_TYPE_I:
+ case CorElementType.ELEMENT_TYPE_U:
+#endif
+ result = GenericLastIndexOf<long>(array, value, adjustedIndex, count);
+ break;
+ case CorElementType.ELEMENT_TYPE_R4:
+ result = GenericLastIndexOf<float>(array, value, adjustedIndex, count);
+ break;
+ case CorElementType.ELEMENT_TYPE_R8:
+ result = GenericLastIndexOf<double>(array, value, adjustedIndex, count);
+ break;
+ default:
+ Debug.Fail("All primitive types should be handled above");
+ break;
}
+
+ return (result >= 0 ? endIndex : lb) + result;
+
+ static int GenericLastIndexOf<T>(Array array, object value, int adjustedIndex, int length) where T : struct, IEquatable<T>
+ => UnsafeArrayAsSpan<T>(array, adjustedIndex, length).LastIndexOf(Unsafe.As<byte, T>(ref value.GetRawData()));
+ }
+ }
+
+ for (int i = startIndex; i >= endIndex; i--)
+ {
+ object? obj = array.GetValue(i);
+ if (obj == null)
+ {
+ if (value == null)
+ return i;
+ }
+ else
+ {
+ if (obj.Equals(value))
+ return i;
}
}
return lb - 1; // Return lb-1 for arrays with negative lower bounds.
if (length <= 1)
return;
-#if !CORERT
- bool r = TrySZReverse(array, index, length);
- if (r)
- return;
-#endif
-
- if (array is object[] objArray)
+ int adjustedIndex = index - lowerBound;
+ switch (array.GetCorElementTypeOfElementType())
{
- Array.Reverse<object>(objArray, index, length);
+ case CorElementType.ELEMENT_TYPE_I1:
+ case CorElementType.ELEMENT_TYPE_U1:
+ case CorElementType.ELEMENT_TYPE_BOOLEAN:
+ UnsafeArrayAsSpan<byte>(array, adjustedIndex, length).Reverse();
+ return;
+ case CorElementType.ELEMENT_TYPE_I2:
+ case CorElementType.ELEMENT_TYPE_U2:
+ case CorElementType.ELEMENT_TYPE_CHAR:
+ UnsafeArrayAsSpan<short>(array, adjustedIndex, length).Reverse();
+ return;
+ case CorElementType.ELEMENT_TYPE_I4:
+ case CorElementType.ELEMENT_TYPE_U4:
+#if !BIT64
+ case CorElementType.ELEMENT_TYPE_I:
+ case CorElementType.ELEMENT_TYPE_U:
+#endif
+ case CorElementType.ELEMENT_TYPE_R4:
+ UnsafeArrayAsSpan<int>(array, adjustedIndex, length).Reverse();
+ return;
+ case CorElementType.ELEMENT_TYPE_I8:
+ case CorElementType.ELEMENT_TYPE_U8:
+#if BIT64
+ case CorElementType.ELEMENT_TYPE_I:
+ case CorElementType.ELEMENT_TYPE_U:
+#endif
+ case CorElementType.ELEMENT_TYPE_R8:
+ UnsafeArrayAsSpan<long>(array, adjustedIndex, length).Reverse();
+ return;
+ case CorElementType.ELEMENT_TYPE_OBJECT:
+ case CorElementType.ELEMENT_TYPE_ARRAY:
+ case CorElementType.ELEMENT_TYPE_SZARRAY:
+ UnsafeArrayAsSpan<object>(array, adjustedIndex, length).Reverse();
+ return;
}
- else
+
+ int i = index;
+ int j = index + length - 1;
+ while (i < j)
{
- int i = index;
- int j = index + length - 1;
- while (i < j)
- {
- object? temp = array.GetValue(i);
- array.SetValue(array.GetValue(j), i);
- array.SetValue(temp, j);
- i++;
- j--;
- }
+ object? temp = array.GetValue(i);
+ array.SetValue(array.GetValue(j), i);
+ array.SetValue(temp, j);
+ i++;
+ j--;
}
}
if (keys.Length - (index - keysLowerBound) < length || (items != null && (index - keysLowerBound) > items.Length - length))
ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_InvalidOffLen);
- if (length > 1)
+ if (length <= 1)
+ return;
+
+ comparer ??= Comparer.Default;
+
+ if (keys is object[] objKeys)
{
- comparer ??= Comparer.Default;
+ object[]? objItems = items as object[];
+ if (items == null || objItems != null)
+ {
+ new SorterObjectArray(objKeys, objItems, comparer).Sort(index, length);
+ return;
+ }
+ }
- if (comparer == Comparer.Default)
+ if (comparer == Comparer.Default)
+ {
+ CorElementType et = keys.GetCorElementTypeOfElementType();
+ if (items == null || items.GetCorElementTypeOfElementType() == et)
{
- switch (Type.GetTypeCode(keys.GetType().GetElementType()))
+ int adjustedIndex = index - keysLowerBound;
+ switch (et)
{
- case TypeCode.Boolean:
- if (TryGenericSort(keys as bool[], items, index, length)) return;
- break;
- case TypeCode.Byte:
- if (TryGenericSort(keys as byte[], items, index, length)) return;
- break;
- case TypeCode.Char:
- if (TryGenericSort(keys as char[], items, index, length)) return;
- break;
- case TypeCode.Double:
- if (TryGenericSort(keys as double[], items, index, length)) return;
- break;
- case TypeCode.Int16:
- if (TryGenericSort(keys as short[], items, index, length)) return;
- break;
- case TypeCode.Int32:
- if (TryGenericSort(keys as int[], items, index, length)) return;
- break;
- case TypeCode.Int64:
- if (TryGenericSort(keys as long[], items, index, length)) return;
- break;
- case TypeCode.SByte:
- if (TryGenericSort(keys as sbyte[], items, index, length)) return;
- break;
- case TypeCode.Single:
- if (TryGenericSort(keys as float[], items, index, length)) return;
- break;
- case TypeCode.String:
- if (TryGenericSort(keys as string[], items, index, length)) return;
- break;
- case TypeCode.UInt16:
- if (TryGenericSort(keys as ushort[], items, index, length)) return;
- break;
- case TypeCode.UInt32:
- if (TryGenericSort(keys as uint[], items, index, length)) return;
- break;
- case TypeCode.UInt64:
- if (TryGenericSort(keys as ulong[], items, index, length)) return;
+ case CorElementType.ELEMENT_TYPE_I1:
+ GenericSort<sbyte>(keys, items, adjustedIndex, length);
+ return;
+ case CorElementType.ELEMENT_TYPE_U1:
+ case CorElementType.ELEMENT_TYPE_BOOLEAN:
+ GenericSort<byte>(keys, items, adjustedIndex, length);
+ return;
+ case CorElementType.ELEMENT_TYPE_I2:
+ GenericSort<short>(keys, items, adjustedIndex, length);
+ return;
+ case CorElementType.ELEMENT_TYPE_U2:
+ case CorElementType.ELEMENT_TYPE_CHAR:
+ GenericSort<ushort>(keys, items, adjustedIndex, length);
+ return;
+ case CorElementType.ELEMENT_TYPE_I4:
+ GenericSort<int>(keys, items, adjustedIndex, length);
+ return;
+ case CorElementType.ELEMENT_TYPE_U4:
+ GenericSort<uint>(keys, items, adjustedIndex, length);
+ return;
+ case CorElementType.ELEMENT_TYPE_I8:
+ GenericSort<long>(keys, items, adjustedIndex, length);
+ return;
+ case CorElementType.ELEMENT_TYPE_U8:
+ GenericSort<ulong>(keys, items, adjustedIndex, length);
+ return;
+ case CorElementType.ELEMENT_TYPE_R4:
+ GenericSort<float>(keys, items, adjustedIndex, length);
+ return;
+ case CorElementType.ELEMENT_TYPE_R8:
+ GenericSort<double>(keys, items, adjustedIndex, length);
+ return;
+ case CorElementType.ELEMENT_TYPE_I:
+ case CorElementType.ELEMENT_TYPE_U:
+ // IntPtr/UIntPtr does not implement IComparable
break;
}
- static bool TryGenericSort<TKey>(TKey[]? keys, Array? items, int index, int length)
+ static void GenericSort<T>(Array keys, Array? items, int adjustedIndex, int length) where T: struct
{
- if (keys != null)
+ Span<T> keysSpan = UnsafeArrayAsSpan<T>(keys, adjustedIndex, length);
+ if (items != null)
{
- if (items is null)
- {
- Sort(keys, index, length);
- return true;
- }
-
- switch (Type.GetTypeCode(items.GetType().GetElementType()))
- {
- case TypeCode.Boolean:
- if (items is bool[] boolItems) { Sort(keys, boolItems, index, length); return true; }
- break;
- case TypeCode.Byte:
- if (items is byte[] byteItems) { Sort(keys, byteItems, index, length); return true; }
- break;
- case TypeCode.Char:
- if (items is char[] charItems) { Sort(keys, charItems, index, length); return true; }
- break;
- case TypeCode.Double:
- if (items is double[] doubleItems) { Sort(keys, doubleItems, index, length); return true; }
- break;
- case TypeCode.Int16:
- if (items is short[] shortItems) { Sort(keys, shortItems, index, length); return true; }
- break;
- case TypeCode.Int32:
- if (items is int[] intItems) { Sort(keys, intItems, index, length); return true; }
- break;
- case TypeCode.Int64:
- if (items is long[] longItems) { Sort(keys, longItems, index, length); return true; }
- break;
- case TypeCode.SByte:
- if (items is sbyte[] sbyteItems) { Sort(keys, sbyteItems, index, length); return true; }
- break;
- case TypeCode.Single:
- if (items is float[] floatItems) { Sort(keys, floatItems, index, length); return true; }
- break;
- case TypeCode.String:
- if (items is string[] stringItems) { Sort(keys, stringItems, index, length); return true; }
- break;
- case TypeCode.UInt16:
- if (items is ushort[] ushortItems) { Sort(keys, ushortItems, index, length); return true; }
- break;
- case TypeCode.UInt32:
- if (items is uint[] uintItems) { Sort(keys, uintItems, index, length); return true; }
- break;
- case TypeCode.UInt64:
- if (items is ulong[] ulongItems) { Sort(keys, ulongItems, index, length); return true; }
- break;
- }
+ keysSpan.Sort<T, T>(UnsafeArrayAsSpan<T>(items, adjustedIndex, length));
+ }
+ else
+ {
+ keysSpan.Sort<T>();
}
-
- return false;
}
}
-
- object[]? objKeys = keys as object[];
- object[]? objItems = null;
- if (objKeys != null)
- objItems = items as object[];
-
- if (objKeys != null && (items == null || objItems != null))
- {
- new SorterObjectArray(objKeys, objItems, comparer).Sort(index, length);
- }
- else
- {
- new SorterGenericArray(keys, items, comparer).Sort(index, length);
- }
}
+
+ new SorterGenericArray(keys, items, comparer).Sort(index, length);
}
public static void Sort<T>(T[] array)
return true;
}
-#if !CORERT
// Private value type used by the Sort methods.
private readonly struct SorterObjectArray
{
this.comparer = comparer;
}
- internal void SwapIfGreaterWithItems(int a, int b)
+ internal void SwapIfGreater(int a, int b)
{
if (a != b)
{
}
if (partitionSize == 2)
{
- SwapIfGreaterWithItems(lo, hi);
+ SwapIfGreater(lo, hi);
return;
}
if (partitionSize == 3)
{
- SwapIfGreaterWithItems(lo, hi - 1);
- SwapIfGreaterWithItems(lo, hi);
- SwapIfGreaterWithItems(hi - 1, hi);
+ SwapIfGreater(lo, hi - 1);
+ SwapIfGreater(lo, hi);
+ SwapIfGreater(hi - 1, hi);
return;
}
// Compute median-of-three. But also partition them, since we've done the comparison.
int mid = lo + (hi - lo) / 2;
// Sort lo, mid and hi appropriately, then pick mid as the pivot.
- SwapIfGreaterWithItems(lo, mid);
- SwapIfGreaterWithItems(lo, hi);
- SwapIfGreaterWithItems(mid, hi);
+ SwapIfGreater(lo, mid);
+ SwapIfGreater(lo, hi);
+ SwapIfGreater(mid, hi);
object pivot = keys[mid];
Swap(mid, hi - 1);
this.comparer = comparer;
}
- internal void SwapIfGreaterWithItems(int a, int b)
+ internal void SwapIfGreater(int a, int b)
{
if (a != b)
{
}
if (partitionSize == 2)
{
- SwapIfGreaterWithItems(lo, hi);
+ SwapIfGreater(lo, hi);
return;
}
if (partitionSize == 3)
{
- SwapIfGreaterWithItems(lo, hi - 1);
- SwapIfGreaterWithItems(lo, hi);
- SwapIfGreaterWithItems(hi - 1, hi);
+ SwapIfGreater(lo, hi - 1);
+ SwapIfGreater(lo, hi);
+ SwapIfGreater(hi - 1, hi);
return;
}
// Compute median-of-three. But also partition them, since we've done the comparison.
int mid = lo + (hi - lo) / 2;
- SwapIfGreaterWithItems(lo, mid);
- SwapIfGreaterWithItems(lo, hi);
- SwapIfGreaterWithItems(mid, hi);
+ SwapIfGreater(lo, mid);
+ SwapIfGreater(lo, hi);
+ SwapIfGreater(mid, hi);
object? pivot = keys.GetValue(mid);
Swap(mid, hi - 1);
}
}
+ private static Span<T> UnsafeArrayAsSpan<T>(Array array, int adjustedIndex, int length) =>
+ new Span<T>(ref Unsafe.As<byte, T>(ref array.GetRawArrayData()), array.Length).Slice(adjustedIndex, length);
+
+#if !CORERT
public IEnumerator GetEnumerator()
{
int lowerBound = GetLowerBound(0);