Skip to content

Commit

Permalink
Merge pull request #519 from CommunityToolkit/dev/ref-fields-memory
Browse files Browse the repository at this point in the history
Use ref fields on .NET 7 for all span-like types
  • Loading branch information
Sergio0694 committed Dec 12, 2022
2 parents 839fd64 + 47b8fe4 commit a7cfaa2
Show file tree
Hide file tree
Showing 9 changed files with 483 additions and 82 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,17 @@ namespace CommunityToolkit.HighPerformance.Enumerables;
/// <typeparam name="T">The type of items to enumerate.</typeparam>
public readonly ref struct ReadOnlyRefEnumerable<T>
{
#if NETSTANDARD2_1_OR_GREATER
#if NET7_0_OR_GREATER
/// <summary>
/// The <typeparamref name="T"/> reference for the <see cref="ReadOnlyRefEnumerable{T}"/> instance.
/// </summary>
private readonly ref readonly T reference;

/// <summary>
/// The length of the current sequence.
/// </summary>
private readonly int length;
#elif NETSTANDARD2_1_OR_GREATER
/// <summary>
/// The <see cref="ReadOnlySpan{T}"/> instance pointing to the first item in the target memory area.
/// </summary>
Expand Down Expand Up @@ -51,6 +61,7 @@ public readonly ref struct ReadOnlyRefEnumerable<T>
private readonly int step;

#if NETSTANDARD2_1_OR_GREATER
#if !NET7_0_OR_GREATER
/// <summary>
/// Initializes a new instance of the <see cref="ReadOnlyRefEnumerable{T}"/> struct.
/// </summary>
Expand All @@ -59,9 +70,15 @@ public readonly ref struct ReadOnlyRefEnumerable<T>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private ReadOnlyRefEnumerable(ReadOnlySpan<T> span, int step)
{
#if NET7_0_OR_GREATER
this.reference = ref MemoryMarshal.GetReference(span);
this.length = span.Length;
#else
this.span = span;
#endif
this.step = step;
}
#endif

/// <summary>
/// Initializes a new instance of the <see cref="ReadOnlyRefEnumerable{T}"/> struct.
Expand All @@ -72,8 +89,14 @@ private ReadOnlyRefEnumerable(ReadOnlySpan<T> span, int step)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal ReadOnlyRefEnumerable(in T reference, int length, int step)
{
#if NET7_0_OR_GREATER
this.reference = ref reference;
this.length = length;
this.step = step;
#else
this.span = MemoryMarshal.CreateReadOnlySpan(ref Unsafe.AsRef(reference), length);
this.step = step;
#endif
}

/// <summary>
Expand Down Expand Up @@ -124,7 +147,9 @@ internal ReadOnlyRefEnumerable(object? instance, IntPtr offset, int length, int
public int Length
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
#if NETSTANDARD2_1_OR_GREATER
#if NET7_0_OR_GREATER
get => this.length;
#elif NETSTANDARD2_1_OR_GREATER
get => this.span.Length;
#else
get => this.length;
Expand All @@ -149,7 +174,9 @@ public ref readonly T this[int index]
ThrowHelper.ThrowIndexOutOfRangeException();
}

#if NETSTANDARD2_1_OR_GREATER
#if NET7_0_OR_GREATER
ref T r0 = ref Unsafe.AsRef(in this.reference);
#elif NETSTANDARD2_1_OR_GREATER
ref T r0 = ref MemoryMarshal.GetReference(this.span);
#else
ref T r0 = ref RuntimeHelpers.GetObjectDataAtOffsetOrPointerReference<T>(this.instance, this.offset);
Expand Down Expand Up @@ -181,7 +208,9 @@ public ref readonly T this[Index index]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Enumerator GetEnumerator()
{
#if NETSTANDARD2_1_OR_GREATER
#if NET7_0_OR_GREATER
return new(in this.reference, this.length, this.step);
#elif NETSTANDARD2_1_OR_GREATER
return new(this.span, this.step);
#else
return new(this.instance, this.offset, this.length, this.step);
Expand All @@ -197,7 +226,26 @@ public Enumerator GetEnumerator()
/// </exception>
public void CopyTo(RefEnumerable<T> destination)
{
#if NETSTANDARD2_1_OR_GREATER
#if NET7_0_OR_GREATER
if (this.step == 1)
{
destination.CopyFrom(MemoryMarshal.CreateReadOnlySpan(ref Unsafe.AsRef(in this.reference), this.length));

return;
}

if (destination.Step == 1)
{
CopyTo(MemoryMarshal.CreateSpan(ref destination.Reference, destination.Length));

return;
}

ref T sourceRef = ref Unsafe.AsRef(in this.reference);
ref T destinationRef = ref destination.Reference;
int sourceLength = this.length;
int destinationLength = destination.Length;
#elif NETSTANDARD2_1_OR_GREATER
if (this.step == 1)
{
destination.CopyFrom(this.span);
Expand Down Expand Up @@ -238,7 +286,10 @@ public void CopyTo(RefEnumerable<T> destination)
/// <returns>Whether or not the operation was successful.</returns>
public bool TryCopyTo(RefEnumerable<T> destination)
{
#if NETSTANDARD2_1_OR_GREATER
#if NET7_0_OR_GREATER
int sourceLength = this.length;
int destinationLength = destination.Length;
#elif NETSTANDARD2_1_OR_GREATER
int sourceLength = this.span.Length;
int destinationLength = destination.Span.Length;
#else
Expand All @@ -265,7 +316,17 @@ public bool TryCopyTo(RefEnumerable<T> destination)
/// </exception>
public void CopyTo(Span<T> destination)
{
#if NETSTANDARD2_1_OR_GREATER
#if NET7_0_OR_GREATER
if (this.step == 1)
{
MemoryMarshal.CreateReadOnlySpan(ref Unsafe.AsRef(in this.reference), this.length).CopyTo(destination);

return;
}

ref T sourceRef = ref Unsafe.AsRef(in this.reference);
int length = this.length;
#elif NETSTANDARD2_1_OR_GREATER
if (this.step == 1)
{
this.span.CopyTo(destination);
Expand Down Expand Up @@ -296,7 +357,9 @@ public void CopyTo(Span<T> destination)
/// <returns>Whether or not the operation was successful.</returns>
public bool TryCopyTo(Span<T> destination)
{
#if NETSTANDARD2_1_OR_GREATER
#if NET7_0_OR_GREATER
int length = this.length;
#elif NETSTANDARD2_1_OR_GREATER
int length = this.span.Length;
#else
int length = this.length;
Expand All @@ -315,7 +378,9 @@ public bool TryCopyTo(Span<T> destination)
/// <inheritdoc cref="RefEnumerable{T}.ToArray"/>
public T[] ToArray()
{
#if NETSTANDARD2_1_OR_GREATER
#if NET7_0_OR_GREATER
int length = this.length;
#elif NETSTANDARD2_1_OR_GREATER
int length = this.span.Length;
#else
int length = this.length;
Expand All @@ -341,7 +406,9 @@ public T[] ToArray()
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator ReadOnlyRefEnumerable<T>(RefEnumerable<T> enumerable)
{
#if NETSTANDARD2_1_OR_GREATER
#if NET7_0_OR_GREATER
return new(in enumerable.Reference, enumerable.Length, enumerable.Step);
#elif NETSTANDARD2_1_OR_GREATER
return new(enumerable.Span, enumerable.Step);
#else
return new(enumerable.Instance, enumerable.Offset, enumerable.Length, enumerable.Step);
Expand All @@ -353,7 +420,13 @@ public static implicit operator ReadOnlyRefEnumerable<T>(RefEnumerable<T> enumer
/// </summary>
public ref struct Enumerator
{
#if NETSTANDARD2_1_OR_GREATER
#if NET7_0_OR_GREATER
/// <inheritdoc cref="ReadOnlyRefEnumerable{T}.reference"/>
private readonly ref readonly T reference;

/// <inheritdoc cref="ReadOnlyRefEnumerable{T}.length"/>
private readonly int length;
#elif NETSTANDARD2_1_OR_GREATER
/// <inheritdoc cref="ReadOnlyRefEnumerable{T}.span"/>
private readonly ReadOnlySpan<T> span;
#else
Expand All @@ -375,7 +448,22 @@ public ref struct Enumerator
/// </summary>
private int position;

#if NETSTANDARD2_1_OR_GREATER
#if NET7_0_OR_GREATER
/// <summary>
/// Initializes a new instance of the <see cref="Enumerator"/> struct.
/// </summary>
/// <param name="reference">The <typeparamref name="T"/> reference to the first item of the sequence.</param>
/// <param name="length">The length of the sequence.</param>
/// <param name="step">The distance between items in the sequence to enumerate.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal Enumerator(in T reference, int length, int step)
{
this.reference = ref reference;
this.length = length;
this.step = step;
this.position = -1;
}
#elif NETSTANDARD2_1_OR_GREATER
/// <summary>
/// Initializes a new instance of the <see cref="Enumerator"/> struct.
/// </summary>
Expand Down Expand Up @@ -411,7 +499,9 @@ internal Enumerator(object? instance, IntPtr offset, int length, int step)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool MoveNext()
{
#if NETSTANDARD2_1_OR_GREATER
#if NET7_0_OR_GREATER
return ++this.position < this.length;
#elif NETSTANDARD2_1_OR_GREATER
return ++this.position < this.span.Length;
#else
return ++this.position < this.length;
Expand All @@ -424,7 +514,9 @@ public readonly ref readonly T Current
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
#if NETSTANDARD2_1_OR_GREATER
#if NET7_0_OR_GREATER
ref T r0 = ref Unsafe.AsRef(in this.reference);
#elif NETSTANDARD2_1_OR_GREATER
ref T r0 = ref this.span.DangerousGetReference();
#else
ref T r0 = ref RuntimeHelpers.GetObjectDataAtOffsetOrPointerReference<T>(this.instance, this.offset);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,10 +81,22 @@ public readonly Item Current
[EditorBrowsable(EditorBrowsableState.Never)]
public readonly ref struct Item
{
#if NET7_0_OR_GREATER
/// <summary>
/// The <typeparamref name="T"/> reference for the <see cref="Item"/> instance.
/// </summary>
private readonly ref readonly T reference;

/// <summary>
/// The index of the current <see cref="Item"/> instance.
/// </summary>
private readonly int index;
#else
/// <summary>
/// The source <see cref="ReadOnlySpan{T}"/> instance.
/// </summary>
private readonly ReadOnlySpan<T> span;
#endif

#if NETSTANDARD2_1_OR_GREATER
/// <summary>
Expand All @@ -95,7 +107,12 @@ public readonly ref struct Item
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Item(ref T value, int index)
{
#if NET7_0_OR_GREATER
this.reference = ref value;
this.index = index;
#else
this.span = MemoryMarshal.CreateReadOnlySpan(ref value, index);
#endif
}
#else
/// <summary>
Expand Down Expand Up @@ -124,7 +141,9 @@ public ref readonly T Value
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
#if NETSTANDARD2_1_OR_GREATER
#if NET7_0_OR_GREATER
return ref this.reference;
#elif NETSTANDARD2_1_OR_GREATER
return ref MemoryMarshal.GetReference(this.span);
#else
ref T r0 = ref MemoryMarshal.GetReference(this.span);
Expand All @@ -143,7 +162,9 @@ public int Index
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
#if NETSTANDARD2_1_OR_GREATER
#if NET7_0_OR_GREATER
return this.index;
#elif NETSTANDARD2_1_OR_GREATER
return this.span.Length;
#else
return this.index;
Expand Down
Loading

0 comments on commit a7cfaa2

Please sign in to comment.