[MethodImpl(MethodImplOptions.InternalCall)]
private static extern unsafe Array InternalCreate(RuntimeType elementType, int rank, int* pLengths, int* pLowerBounds);
- // Copies length elements from sourceArray, starting at index 0, to
- // destinationArray, starting at index 0.
- //
- public static unsafe void Copy(Array sourceArray, Array destinationArray, int length)
- {
- if (sourceArray == null)
- ThrowHelper.ThrowArgumentNullException(ExceptionArgument.sourceArray);
- if (destinationArray == null)
- ThrowHelper.ThrowArgumentNullException(ExceptionArgument.destinationArray);
-
- MethodTable* pMT = RuntimeHelpers.GetMethodTable(sourceArray);
- if (pMT == RuntimeHelpers.GetMethodTable(destinationArray) &&
- !pMT->IsMultiDimensionalArray &&
- (uint)length <= sourceArray.NativeLength &&
- (uint)length <= destinationArray.NativeLength)
- {
- nuint byteCount = (uint)length * (nuint)pMT->ComponentSize;
- ref byte src = ref Unsafe.As<RawArrayData>(sourceArray).Data;
- ref byte dst = ref Unsafe.As<RawArrayData>(destinationArray).Data;
-
- if (pMT->ContainsGCPointers)
- Buffer.BulkMoveWithWriteBarrier(ref dst, ref src, byteCount);
- else
- Buffer.Memmove(ref dst, ref src, byteCount);
-
- // GC.KeepAlive(sourceArray) not required. pMT kept alive via sourceArray
- return;
- }
-
- // Less common
- Copy(sourceArray, sourceArray.GetLowerBound(0), destinationArray, destinationArray.GetLowerBound(0), length, reliable: false);
- }
-
- // Copies length elements from sourceArray, starting at sourceIndex, to
- // destinationArray, starting at destinationIndex.
- //
- public static unsafe void Copy(Array sourceArray, int sourceIndex, Array destinationArray, int destinationIndex, int length)
- {
- if (sourceArray != null && destinationArray != null)
- {
- MethodTable* pMT = RuntimeHelpers.GetMethodTable(sourceArray);
- if (pMT == RuntimeHelpers.GetMethodTable(destinationArray) &&
- !pMT->IsMultiDimensionalArray &&
- length >= 0 && sourceIndex >= 0 && destinationIndex >= 0 &&
- (uint)(sourceIndex + length) <= sourceArray.NativeLength &&
- (uint)(destinationIndex + length) <= destinationArray.NativeLength)
- {
- nuint elementSize = (nuint)pMT->ComponentSize;
- nuint byteCount = (uint)length * elementSize;
- ref byte src = ref Unsafe.AddByteOffset(ref Unsafe.As<RawArrayData>(sourceArray).Data, (uint)sourceIndex * elementSize);
- ref byte dst = ref Unsafe.AddByteOffset(ref Unsafe.As<RawArrayData>(destinationArray).Data, (uint)destinationIndex * elementSize);
-
- if (pMT->ContainsGCPointers)
- Buffer.BulkMoveWithWriteBarrier(ref dst, ref src, byteCount);
- else
- Buffer.Memmove(ref dst, ref src, byteCount);
-
- // GC.KeepAlive(sourceArray) not required. pMT kept alive via sourceArray
- return;
- }
- }
-
- // Less common
- Copy(sourceArray!, sourceIndex, destinationArray!, destinationIndex, length, reliable: false);
- }
- private static unsafe void Copy(Array sourceArray, int sourceIndex, Array destinationArray, int destinationIndex, int length, bool reliable)
+ private static unsafe void CopyImpl(Array sourceArray, int sourceIndex, Array destinationArray, int destinationIndex, int length, bool reliable)
{
if (sourceArray == null)
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.sourceArray);
// It will up-cast, assuming the array types are correct.
public static void ConstrainedCopy(Array sourceArray, int sourceIndex, Array destinationArray, int destinationIndex, int length)
{
- Copy(sourceArray, sourceIndex, destinationArray, destinationIndex, length, reliable: true);
+ CopyImpl(sourceArray, sourceIndex, destinationArray, destinationIndex, length, reliable: true);
}
/// <summary>
public bool HasTypeEquivalence => (Flags & enum_flag_HasTypeEquivalence) != 0;
+ internal static bool AreSameType(MethodTable* mt1, MethodTable* mt2) => mt1 == mt2;
+
public bool HasDefaultConstructor => (Flags & (enum_flag_HasComponentSize | enum_flag_HasDefaultCtor)) == enum_flag_HasDefaultCtor;
public bool IsMultiDimensionalArray
}
}
+ internal bool IsMultiDimensionalArray
+ {
+ get
+ {
+ Debug.Assert(HasComponentSize);
+ // See comment on RawArrayData for details
+ return BaseSize > (uint)(3 * sizeof(IntPtr));
+ }
+ }
+
internal bool IsGeneric
{
get
}
}
- internal bool HasGCPointers
+ internal bool ContainsGCPointers
{
get
{
return ((pThis->_uFlags | pOther->_uFlags) & (uint)EETypeFlags.ComplexCastingMask) == 0;
}
+ internal static bool AreSameType(MethodTable* mt1, MethodTable* mt2)
+ {
+ if (mt1 == mt2)
+ return true;
+
+ return mt1->IsEquivalentTo(mt2);
+ }
+
internal bool IsEquivalentTo(MethodTable* pOtherEEType)
{
fixed (MethodTable* pThis = &this)
// Copy the unboxed value type data into the new object.
// Perform any write barriers necessary for embedded reference fields.
- if (pEEType->HasGCPointers)
+ if (pEEType->ContainsGCPointers)
{
InternalCalls.RhBulkMoveWithWriteBarrier(ref result.GetRawData(), ref dataAdjustedForNullable, pEEType->ValueTypeSize);
}
ref byte fields = ref obj.GetRawData();
- if (pEEType->HasGCPointers)
+ if (pEEType->ContainsGCPointers)
{
// Copy the boxed fields into the new location in a GC safe manner
InternalCalls.RhBulkMoveWithWriteBarrier(ref data, ref fields, pEEType->ValueTypeSize);
CopyImpl(sourceArray, sourceIndex, destinationArray, destinationIndex, length, reliable: true);
}
- public static void Copy(Array sourceArray, Array destinationArray, int length)
- {
- if (sourceArray is null)
- ThrowHelper.ThrowArgumentNullException(ExceptionArgument.sourceArray);
- if (destinationArray is null)
- ThrowHelper.ThrowArgumentNullException(ExceptionArgument.destinationArray);
-
- EETypePtr eeType = sourceArray.GetEETypePtr();
- if (eeType.FastEquals(destinationArray.GetEETypePtr()) &&
- eeType.IsSzArray &&
- (uint)length <= sourceArray.NativeLength &&
- (uint)length <= destinationArray.NativeLength)
- {
- nuint byteCount = (uint)length * (nuint)eeType.ComponentSize;
- ref byte src = ref Unsafe.As<RawArrayData>(sourceArray).Data;
- ref byte dst = ref Unsafe.As<RawArrayData>(destinationArray).Data;
-
- if (eeType.HasPointers)
- Buffer.BulkMoveWithWriteBarrier(ref dst, ref src, byteCount);
- else
- Buffer.Memmove(ref dst, ref src, byteCount);
-
- // GC.KeepAlive(sourceArray) not required. pMT kept alive via sourceArray
- return;
- }
-
- // Less common
- CopyImpl(sourceArray, sourceArray.GetLowerBound(0), destinationArray, destinationArray.GetLowerBound(0), length, reliable: false);
- }
-
- public static unsafe void Copy(Array sourceArray, int sourceIndex, Array destinationArray, int destinationIndex, int length)
- {
- if (sourceArray != null && destinationArray != null)
- {
- EETypePtr eeType = sourceArray.GetEETypePtr();
- if (eeType.FastEquals(destinationArray.GetEETypePtr()) &&
- eeType.IsSzArray &&
- length >= 0 && sourceIndex >= 0 && destinationIndex >= 0 &&
- (uint)(sourceIndex + length) <= sourceArray.NativeLength &&
- (uint)(destinationIndex + length) <= destinationArray.NativeLength)
- {
- nuint elementSize = (nuint)eeType.ComponentSize;
- nuint byteCount = (uint)length * elementSize;
- ref byte src = ref Unsafe.AddByteOffset(ref Unsafe.As<RawArrayData>(sourceArray).Data, (uint)sourceIndex * elementSize);
- ref byte dst = ref Unsafe.AddByteOffset(ref Unsafe.As<RawArrayData>(destinationArray).Data, (uint)destinationIndex * elementSize);
-
- if (eeType.HasPointers)
- Buffer.BulkMoveWithWriteBarrier(ref dst, ref src, byteCount);
- else
- Buffer.Memmove(ref dst, ref src, byteCount);
-
- // GC.KeepAlive(sourceArray) not required. pMT kept alive via sourceArray
- return;
- }
- }
-
- // Less common
- CopyImpl(sourceArray!, sourceIndex, destinationArray!, destinationIndex, length, reliable: false);
- }
//
// Funnel for all the Array.Copy() overloads. The "reliable" parameter indicates whether the caller for ConstrainedCopy()
{
if (RuntimeImports.AreTypesEquivalent(sourceElementEEType, destinationElementEEType))
{
- if (sourceElementEEType.HasPointers)
+ if (sourceElementEEType.ContainsGCPointers)
{
CopyImplValueTypeArrayWithInnerGcRefs(sourceArray, sourceIndex, destinationArray, destinationIndex, length, reliable);
}
//
private static unsafe void CopyImplValueTypeArrayNoInnerGcRefs(Array sourceArray, int sourceIndex, Array destinationArray, int destinationIndex, int length)
{
- Debug.Assert((sourceArray.ElementEEType.IsValueType && !sourceArray.ElementEEType.HasPointers) ||
+ Debug.Assert((sourceArray.ElementEEType.IsValueType && !sourceArray.ElementEEType.ContainsGCPointers) ||
sourceArray.ElementEEType.IsPointer);
- Debug.Assert((destinationArray.ElementEEType.IsValueType && !destinationArray.ElementEEType.HasPointers) ||
+ Debug.Assert((destinationArray.ElementEEType.IsValueType && !destinationArray.ElementEEType.ContainsGCPointers) ||
destinationArray.ElementEEType.IsPointer);
// Copy scenario: ValueType-array to value-type array with no embedded gc-refs.
nuint totalByteLength = eeType.ComponentSize * array.NativeLength;
ref byte pStart = ref MemoryMarshal.GetArrayDataReference(array);
- if (!eeType.HasPointers)
+ if (!eeType.ContainsGCPointers)
{
SpanHelpers.ClearWithoutReferences(ref pStart, totalByteLength);
}
ref byte ptr = ref Unsafe.AddByteOffset(ref p, (uint)offset * elementSize);
nuint byteLength = (uint)length * elementSize;
- if (eeType.HasPointers)
+ if (eeType.ContainsGCPointers)
{
Debug.Assert(byteLength % (nuint)sizeof(IntPtr) == 0);
SpanHelpers.ClearWithReferences(ref Unsafe.As<byte, IntPtr>(ref ptr), byteLength / (uint)sizeof(IntPtr));
return (int)_value->HashCode;
}
- //
- // Faster version of Equals for use on EETypes that are known not to be null and where the "match" case is the hot path.
- //
- public bool FastEquals(EETypePtr other)
- {
- Debug.Assert(!this.IsNull);
- Debug.Assert(!other.IsNull);
-
- // Fast check for raw equality before making call to helper.
- if (this.RawValue == other.RawValue)
- return true;
- return RuntimeImports.AreTypesEquivalent(this, other);
- }
-
// Caution: You cannot safely compare RawValue's as RH does NOT unify EETypes. Use the == or Equals() methods exposed by EETypePtr itself.
internal IntPtr RawValue
{
}
}
- // Has internal gc pointers.
- internal bool HasPointers
+ // Instance contains pointers to managed objects.
+ internal bool ContainsGCPointers
{
get
{
- return _value->HasGCPointers;
+ return _value->ContainsGCPointers;
}
}
ref byte src = ref this.GetRawData();
ref byte dst = ref clone.GetRawData();
- if (this.GetEETypePtr().HasPointers)
+ if (this.GetEETypePtr().ContainsGCPointers)
Buffer.BulkMoveWithWriteBarrier(ref dst, ref src, byteCount);
else
Buffer.Memmove(ref dst, ref src, byteCount);
public static bool IsReferenceOrContainsReferences<T>()
{
var pEEType = EETypePtr.EETypePtrOf<T>();
- return !pEEType.IsValueType || pEEType.HasPointers;
+ return !pEEType.IsValueType || pEEType.ContainsGCPointers;
}
[Intrinsic]
// This is used as the approximate implementation of MethodTable::IsBlittable(). It will err in the direction of declaring
// things blittable since it is used for argument validation only.
//
- return !eeType.HasPointers;
+ return !eeType.ContainsGCPointers;
}
public static bool IsBlittable(this RuntimeTypeHandle handle)
internal static bool IsPinnable(object o)
{
- return (o == null) || !o.GetEETypePtr().HasPointers;
+ return (o == null) || !o.GetEETypePtr().ContainsGCPointers;
}
[EditorBrowsable(EditorBrowsableState.Never)]
if (numFields == UseFastHelper)
{
// Sanity check - if there are GC references, we should not be comparing bytes
- Debug.Assert(!this.GetEETypePtr().HasPointers);
+ Debug.Assert(!this.GetEETypePtr().ContainsGCPointers);
// Compare the memory
int valueTypeSize = (int)this.GetEETypePtr().ValueTypeSize;
private static unsafe int FastGetValueTypeHashCodeHelper(MethodTable* type, ref byte data)
{
// Sanity check - if there are GC references, we should not be hashing bytes
- Debug.Assert(!type->HasGCPointers);
+ Debug.Assert(!type->ContainsGCPointers);
int size = (int)type->ValueTypeSize;
int hashCode = 0;
bool isSzArray = isArray ? state.ArrayRank < 1 : false;
int arrayRank = isArray ? state.ArrayRank.Value : 0;
CreateInstanceGCDesc(state, pTemplateEEType, pEEType, baseSize, cbGCDesc, isValueType, isArray, isSzArray, arrayRank);
- Debug.Assert(pEEType->HasGCPointers == (cbGCDesc != 0));
+ Debug.Assert(pEEType->ContainsGCPointers == (cbGCDesc != 0));
// Copy the encoded optional fields buffer to the newly allocated memory, and update the OptionalFields field on the MethodTable
if (cbOptionalFieldsSize > 0)
{
if (cbGCDesc != 0)
{
- pEEType->HasGCPointers = true;
+ pEEType->ContainsGCPointers = true;
if (state.IsArrayOfReferenceTypes)
{
IntPtr* gcDescStart = (IntPtr*)((byte*)pEEType - cbGCDesc);
}
else
{
- pEEType->HasGCPointers = false;
+ pEEType->ContainsGCPointers = false;
}
}
else if (gcBitfield != null)
{
if (cbGCDesc != 0)
{
- pEEType->HasGCPointers = true;
+ pEEType->ContainsGCPointers = true;
CreateGCDesc(gcBitfield, baseSize, isValueType, false, ((void**)pEEType) - 1);
}
else
{
- pEEType->HasGCPointers = false;
+ pEEType->ContainsGCPointers = false;
}
}
else if (pTemplateEEType != null)
{
Buffer.MemoryCopy((byte*)pTemplateEEType - cbGCDesc, (byte*)pEEType - cbGCDesc, cbGCDesc, cbGCDesc);
- pEEType->HasGCPointers = pTemplateEEType->HasGCPointers;
+ pEEType->ContainsGCPointers = pTemplateEEType->ContainsGCPointers;
}
else
{
- pEEType->HasGCPointers = false;
+ pEEType->ContainsGCPointers = false;
}
}
_bitfield = null;
_isReferenceTypeGCLayout = false; // This field is only used for the LowLevelList<bool> path
- _gcdesc = MethodTable->HasGCPointers ? (void**)MethodTable - 1 : null;
+ _gcdesc = MethodTable->ContainsGCPointers ? (void**)MethodTable - 1 : null;
_size = (int)MethodTable->BaseSize;
}
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
+using Internal.Runtime;
#pragma warning disable 8500 // sizeof of managed types
Copy(sourceArray, isourceIndex, destinationArray, idestinationIndex, ilength);
}
+#if !MONO // implementation details of MethodTable
+
+ // Copies length elements from sourceArray, starting at index 0, to
+ // destinationArray, starting at index 0.
+ public static unsafe void Copy(Array sourceArray, Array destinationArray, int length)
+ {
+ if (sourceArray is null)
+ ThrowHelper.ThrowArgumentNullException(ExceptionArgument.sourceArray);
+ if (destinationArray is null)
+ ThrowHelper.ThrowArgumentNullException(ExceptionArgument.destinationArray);
+
+ MethodTable* pMT = RuntimeHelpers.GetMethodTable(sourceArray);
+ if (MethodTable.AreSameType(pMT, RuntimeHelpers.GetMethodTable(destinationArray)) &&
+ !pMT->IsMultiDimensionalArray &&
+ (uint)length <= sourceArray.NativeLength &&
+ (uint)length <= destinationArray.NativeLength)
+ {
+ nuint byteCount = (uint)length * (nuint)pMT->ComponentSize;
+ ref byte src = ref Unsafe.As<RawArrayData>(sourceArray).Data;
+ ref byte dst = ref Unsafe.As<RawArrayData>(destinationArray).Data;
+
+ if (pMT->ContainsGCPointers)
+ Buffer.BulkMoveWithWriteBarrier(ref dst, ref src, byteCount);
+ else
+ Buffer.Memmove(ref dst, ref src, byteCount);
+
+ // GC.KeepAlive(sourceArray) not required. pMT kept alive via sourceArray
+ return;
+ }
+
+ // Less common
+ CopyImpl(sourceArray, sourceArray.GetLowerBound(0), destinationArray, destinationArray.GetLowerBound(0), length, reliable: false);
+ }
+
+ // Copies length elements from sourceArray, starting at sourceIndex, to
+ // destinationArray, starting at destinationIndex.
+ public static unsafe void Copy(Array sourceArray, int sourceIndex, Array destinationArray, int destinationIndex, int length)
+ {
+ if (sourceArray != null && destinationArray != null)
+ {
+ MethodTable* pMT = RuntimeHelpers.GetMethodTable(sourceArray);
+ if (MethodTable.AreSameType(pMT, RuntimeHelpers.GetMethodTable(destinationArray)) &&
+ !pMT->IsMultiDimensionalArray &&
+ length >= 0 && sourceIndex >= 0 && destinationIndex >= 0 &&
+ (uint)(sourceIndex + length) <= sourceArray.NativeLength &&
+ (uint)(destinationIndex + length) <= destinationArray.NativeLength)
+ {
+ nuint elementSize = (nuint)pMT->ComponentSize;
+ nuint byteCount = (uint)length * elementSize;
+ ref byte src = ref Unsafe.AddByteOffset(ref Unsafe.As<RawArrayData>(sourceArray).Data, (uint)sourceIndex * elementSize);
+ ref byte dst = ref Unsafe.AddByteOffset(ref Unsafe.As<RawArrayData>(destinationArray).Data, (uint)destinationIndex * elementSize);
+
+ if (pMT->ContainsGCPointers)
+ Buffer.BulkMoveWithWriteBarrier(ref dst, ref src, byteCount);
+ else
+ Buffer.Memmove(ref dst, ref src, byteCount);
+
+ // GC.KeepAlive(sourceArray) not required. pMT kept alive via sourceArray
+ return;
+ }
+ }
+
+ // Less common
+ CopyImpl(sourceArray!, sourceIndex, destinationArray!, destinationIndex, length, reliable: false);
+ }
+#endif
+
// The various Get values...
public object? GetValue(params int[] indices)
{