Skip to content
This repository has been archived by the owner on Jan 23, 2023. It is now read-only.
/ corefx Public archive

Commit

Permalink
Rename IsReferenceFree to IsReferenceOrContainsReferences (#16093)
Browse files Browse the repository at this point in the history
- Rename IsReferenceFree to IsReferenceOrContainsReferences for consistency with #14047
- Remove IsReferenceFreeCore micro-optimization that is actually hurting. This code runs just once and
it is cheaper to do a check via reflection than to spend time in the JIT to optimize the extra code.
- Re-enabled disables test
  • Loading branch information
jkotas authored Feb 12, 2017
1 parent e1d869f commit 73e25c0
Show file tree
Hide file tree
Showing 4 changed files with 27 additions and 61 deletions.
2 changes: 1 addition & 1 deletion src/System.Memory/src/System/ReadOnlySpan.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<T>())
if (SpanHelpers.IsReferenceOrContainsReferences<T>())
ThrowHelper.ThrowArgumentException_InvalidTypeWithPointersNotSupported(typeof(T));
if (length < 0)
ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start);
Expand Down
28 changes: 14 additions & 14 deletions src/System.Memory/src/System/Span.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<T>())
if (SpanHelpers.IsReferenceOrContainsReferences<T>())
ThrowHelper.ThrowArgumentException_InvalidTypeWithPointersNotSupported(typeof(T));
if (length < 0)
ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start);
Expand Down Expand Up @@ -261,20 +261,20 @@ public unsafe void Clear()
}
else
{
if (SpanHelpers.IsReferenceFree<T>())
{
ref byte b = ref Unsafe.As<T, byte>(ref DangerousGetPinnableReference());

SpanHelpers.ClearPointerSizedWithoutReferences(ref b, byteLength);
}
else
if (SpanHelpers.IsReferenceOrContainsReferences<T>())
{
UIntPtr pointerSizedLength = (UIntPtr)((length * Unsafe.SizeOf<T>()) / sizeof(IntPtr));

ref IntPtr ip = ref Unsafe.As<T, IntPtr>(ref DangerousGetPinnableReference());

SpanHelpers.ClearPointerSizedWithReferences(ref ip, pointerSizedLength);
}
else
{
ref byte b = ref Unsafe.As<T, byte>(ref DangerousGetPinnableReference());

SpanHelpers.ClearPointerSizedWithoutReferences(ref b, byteLength);
}
}
}

Expand Down Expand Up @@ -562,7 +562,7 @@ public static class Span
public static Span<byte> AsBytes<T>(this Span<T> source)
where T : struct
{
if (!SpanHelpers.IsReferenceFree<T>())
if (SpanHelpers.IsReferenceOrContainsReferences<T>())
ThrowHelper.ThrowArgumentException_InvalidTypeWithPointersNotSupported(typeof(T));

int newLength = checked(source.Length * Unsafe.SizeOf<T>());
Expand All @@ -584,7 +584,7 @@ public static Span<byte> AsBytes<T>(this Span<T> source)
public static ReadOnlySpan<byte> AsBytes<T>(this ReadOnlySpan<T> source)
where T : struct
{
if (!SpanHelpers.IsReferenceFree<T>())
if (SpanHelpers.IsReferenceOrContainsReferences<T>())
ThrowHelper.ThrowArgumentException_InvalidTypeWithPointersNotSupported(typeof(T));

int newLength = checked(source.Length * Unsafe.SizeOf<T>());
Expand All @@ -610,10 +610,10 @@ public static Span<TTo> NonPortableCast<TFrom, TTo>(this Span<TFrom> source)
where TFrom : struct
where TTo : struct
{
if (!SpanHelpers.IsReferenceFree<TFrom>())
if (SpanHelpers.IsReferenceOrContainsReferences<TFrom>())
ThrowHelper.ThrowArgumentException_InvalidTypeWithPointersNotSupported(typeof(TFrom));

if (!SpanHelpers.IsReferenceFree<TTo>())
if (SpanHelpers.IsReferenceOrContainsReferences<TTo>())
ThrowHelper.ThrowArgumentException_InvalidTypeWithPointersNotSupported(typeof(TTo));

int newLength = checked((int)((long)source.Length * Unsafe.SizeOf<TFrom>() / Unsafe.SizeOf<TTo>()));
Expand All @@ -639,10 +639,10 @@ public static ReadOnlySpan<TTo> NonPortableCast<TFrom, TTo>(this ReadOnlySpan<TF
where TFrom : struct
where TTo : struct
{
if (!SpanHelpers.IsReferenceFree<TFrom>())
if (SpanHelpers.IsReferenceOrContainsReferences<TFrom>())
ThrowHelper.ThrowArgumentException_InvalidTypeWithPointersNotSupported(typeof(TFrom));

if (!SpanHelpers.IsReferenceFree<TTo>())
if (SpanHelpers.IsReferenceOrContainsReferences<TTo>())
ThrowHelper.ThrowArgumentException_InvalidTypeWithPointersNotSupported(typeof(TTo));

int newLength = checked((int)((long)source.Length * Unsafe.SizeOf<TFrom>() / Unsafe.SizeOf<TTo>()));
Expand Down
57 changes: 12 additions & 45 deletions src/System.Memory/src/System/SpanHelpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,77 +45,44 @@ public static IntPtr Add<T>(this IntPtr start, int index)
}

/// <summary>
/// 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<T>()
/// </summary>
public static bool IsReferenceFree<T>() => PerTypeValues<T>.IsReferenceFree;
public static bool IsReferenceOrContainsReferences<T>() => PerTypeValues<T>.IsReferenceOrContainsReferences;

private static bool IsReferenceFreeCore<T>()
{
// 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);
if (underlyingNullable != null)
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<T>
{
//
// 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<T>();
public static readonly bool IsReferenceOrContainsReferences = IsReferenceOrContainsReferencesCore(typeof(T));

public static readonly T[] EmptyArray = new T[0];

Expand Down
1 change: 0 additions & 1 deletion src/System.Memory/tests/Span/Overflow.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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()
{
//
Expand Down

0 comments on commit 73e25c0

Please sign in to comment.