diff --git a/src/System.Memory/src/System/ReadOnlySpan.cs b/src/System.Memory/src/System/ReadOnlySpan.cs index 80d118c4b8f2..0f3c434fdc10 100644 --- a/src/System.Memory/src/System/ReadOnlySpan.cs +++ b/src/System.Memory/src/System/ReadOnlySpan.cs @@ -111,7 +111,7 @@ public ReadOnlySpan(T[] array, int start, int length) [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe ReadOnlySpan(void* pointer, int length) { - if (!SpanHelpers.IsReferenceFree()) + if (SpanHelpers.IsReferenceOrContainsReferences()) ThrowHelper.ThrowArgumentException_InvalidTypeWithPointersNotSupported(typeof(T)); if (length < 0) ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start); diff --git a/src/System.Memory/src/System/Span.cs b/src/System.Memory/src/System/Span.cs index bc458ef4eacf..7fd8348c99fd 100644 --- a/src/System.Memory/src/System/Span.cs +++ b/src/System.Memory/src/System/Span.cs @@ -111,7 +111,7 @@ public Span(T[] array, int start, int length) [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe Span(void* pointer, int length) { - if (!SpanHelpers.IsReferenceFree()) + if (SpanHelpers.IsReferenceOrContainsReferences()) ThrowHelper.ThrowArgumentException_InvalidTypeWithPointersNotSupported(typeof(T)); if (length < 0) ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start); @@ -261,13 +261,7 @@ public unsafe void Clear() } else { - if (SpanHelpers.IsReferenceFree()) - { - ref byte b = ref Unsafe.As(ref DangerousGetPinnableReference()); - - SpanHelpers.ClearPointerSizedWithoutReferences(ref b, byteLength); - } - else + if (SpanHelpers.IsReferenceOrContainsReferences()) { UIntPtr pointerSizedLength = (UIntPtr)((length * Unsafe.SizeOf()) / sizeof(IntPtr)); @@ -275,6 +269,12 @@ public unsafe void Clear() SpanHelpers.ClearPointerSizedWithReferences(ref ip, pointerSizedLength); } + else + { + ref byte b = ref Unsafe.As(ref DangerousGetPinnableReference()); + + SpanHelpers.ClearPointerSizedWithoutReferences(ref b, byteLength); + } } } @@ -562,7 +562,7 @@ public static class Span public static Span AsBytes(this Span source) where T : struct { - if (!SpanHelpers.IsReferenceFree()) + if (SpanHelpers.IsReferenceOrContainsReferences()) ThrowHelper.ThrowArgumentException_InvalidTypeWithPointersNotSupported(typeof(T)); int newLength = checked(source.Length * Unsafe.SizeOf()); @@ -584,7 +584,7 @@ public static Span AsBytes(this Span source) public static ReadOnlySpan AsBytes(this ReadOnlySpan source) where T : struct { - if (!SpanHelpers.IsReferenceFree()) + if (SpanHelpers.IsReferenceOrContainsReferences()) ThrowHelper.ThrowArgumentException_InvalidTypeWithPointersNotSupported(typeof(T)); int newLength = checked(source.Length * Unsafe.SizeOf()); @@ -610,10 +610,10 @@ public static Span NonPortableCast(this Span source) where TFrom : struct where TTo : struct { - if (!SpanHelpers.IsReferenceFree()) + if (SpanHelpers.IsReferenceOrContainsReferences()) ThrowHelper.ThrowArgumentException_InvalidTypeWithPointersNotSupported(typeof(TFrom)); - if (!SpanHelpers.IsReferenceFree()) + if (SpanHelpers.IsReferenceOrContainsReferences()) ThrowHelper.ThrowArgumentException_InvalidTypeWithPointersNotSupported(typeof(TTo)); int newLength = checked((int)((long)source.Length * Unsafe.SizeOf() / Unsafe.SizeOf())); @@ -639,10 +639,10 @@ public static ReadOnlySpan NonPortableCast(this ReadOnlySpan()) + if (SpanHelpers.IsReferenceOrContainsReferences()) ThrowHelper.ThrowArgumentException_InvalidTypeWithPointersNotSupported(typeof(TFrom)); - if (!SpanHelpers.IsReferenceFree()) + if (SpanHelpers.IsReferenceOrContainsReferences()) ThrowHelper.ThrowArgumentException_InvalidTypeWithPointersNotSupported(typeof(TTo)); int newLength = checked((int)((long)source.Length * Unsafe.SizeOf() / Unsafe.SizeOf())); diff --git a/src/System.Memory/src/System/SpanHelpers.cs b/src/System.Memory/src/System/SpanHelpers.cs index 57d026267c8b..f1da63e034d4 100644 --- a/src/System.Memory/src/System/SpanHelpers.cs +++ b/src/System.Memory/src/System/SpanHelpers.cs @@ -45,48 +45,18 @@ public static IntPtr Add(this IntPtr start, int index) } /// - /// Determine if a type is eligible for storage in unmanaged memory. TODO: To be replaced by a ContainsReference() api. + /// Determine if a type is eligible for storage in unmanaged memory. + /// Portable equivalent of RuntimeHelpers.IsReferenceOrContainsReferences() /// - public static bool IsReferenceFree() => PerTypeValues.IsReferenceFree; + public static bool IsReferenceOrContainsReferences() => PerTypeValues.IsReferenceOrContainsReferences; - private static bool IsReferenceFreeCore() - { - // Under the JIT, these become constant-folded. - if (typeof(T) == typeof(byte)) - return true; - if (typeof(T) == typeof(sbyte)) - return true; - if (typeof(T) == typeof(bool)) - return true; - if (typeof(T) == typeof(char)) - return true; - if (typeof(T) == typeof(short)) - return true; - if (typeof(T) == typeof(ushort)) - return true; - if (typeof(T) == typeof(int)) - return true; - if (typeof(T) == typeof(uint)) - return true; - if (typeof(T) == typeof(long)) - return true; - if (typeof(T) == typeof(ulong)) - return true; - if (typeof(T) == typeof(IntPtr)) - return true; - if (typeof(T) == typeof(UIntPtr)) - return true; - - return IsReferenceFreeCoreSlow(typeof(T)); - } - - private static bool IsReferenceFreeCoreSlow(Type type) + private static bool IsReferenceOrContainsReferencesCore(Type type) { if (type.GetTypeInfo().IsPrimitive) // This is hopefully the common case. All types that return true for this are value types w/out embedded references. - return true; + return false; if (!type.GetTypeInfo().IsValueType) - return false; + return true; // If type is a Nullable<> of something, unwrap it first. Type underlyingNullable = Nullable.GetUnderlyingType(type); @@ -94,28 +64,25 @@ private static bool IsReferenceFreeCoreSlow(Type type) type = underlyingNullable; if (type.GetTypeInfo().IsEnum) - return true; + return false; foreach (FieldInfo field in type.GetTypeInfo().DeclaredFields) { if (field.IsStatic) continue; - if (!IsReferenceFreeCoreSlow(field.FieldType)) - return false; + if (IsReferenceOrContainsReferencesCore(field.FieldType)) + return true; } - return true; + return false; } public static class PerTypeValues { // // Latch to ensure that excruciatingly expensive validation check for constructing a Span around a raw pointer is done - // only once per type (unless of course, the validation fails.) - // - // false == not yet computed or found to be not reference free. - // true == confirmed reference free + // only once per type. // - public static readonly bool IsReferenceFree = IsReferenceFreeCore(); + public static readonly bool IsReferenceOrContainsReferences = IsReferenceOrContainsReferencesCore(typeof(T)); public static readonly T[] EmptyArray = new T[0]; diff --git a/src/System.Memory/tests/Span/Overflow.cs b/src/System.Memory/tests/Span/Overflow.cs index efafc7fb1074..bb24a81b4ea6 100644 --- a/src/System.Memory/tests/Span/Overflow.cs +++ b/src/System.Memory/tests/Span/Overflow.cs @@ -10,7 +10,6 @@ namespace System.SpanTests { public static partial class SpanTests { - [Fact(Skip = "Issue # 14484 - Disabling IndexOverflow test until FastSpan has a GetItem implementation. Related: issue # 13681")] public static void IndexOverflow() { //