Skip to content

Commit 47b8fe4

Browse files
committed
Use ref fields in [ReadOnly]RefEnumerable<T>-s
1 parent 434f482 commit 47b8fe4

File tree

2 files changed

+237
-31
lines changed

2 files changed

+237
-31
lines changed

src/CommunityToolkit.HighPerformance/Enumerables/ReadOnlyRefEnumerable{T}.cs

Lines changed: 106 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,17 @@ namespace CommunityToolkit.HighPerformance.Enumerables;
2121
/// <typeparam name="T">The type of items to enumerate.</typeparam>
2222
public readonly ref struct ReadOnlyRefEnumerable<T>
2323
{
24-
#if NETSTANDARD2_1_OR_GREATER
24+
#if NET7_0_OR_GREATER
25+
/// <summary>
26+
/// The <typeparamref name="T"/> reference for the <see cref="ReadOnlyRefEnumerable{T}"/> instance.
27+
/// </summary>
28+
private readonly ref readonly T reference;
29+
30+
/// <summary>
31+
/// The length of the current sequence.
32+
/// </summary>
33+
private readonly int length;
34+
#elif NETSTANDARD2_1_OR_GREATER
2535
/// <summary>
2636
/// The <see cref="ReadOnlySpan{T}"/> instance pointing to the first item in the target memory area.
2737
/// </summary>
@@ -51,6 +61,7 @@ public readonly ref struct ReadOnlyRefEnumerable<T>
5161
private readonly int step;
5262

5363
#if NETSTANDARD2_1_OR_GREATER
64+
#if !NET7_0_OR_GREATER
5465
/// <summary>
5566
/// Initializes a new instance of the <see cref="ReadOnlyRefEnumerable{T}"/> struct.
5667
/// </summary>
@@ -59,9 +70,15 @@ public readonly ref struct ReadOnlyRefEnumerable<T>
5970
[MethodImpl(MethodImplOptions.AggressiveInlining)]
6071
private ReadOnlyRefEnumerable(ReadOnlySpan<T> span, int step)
6172
{
73+
#if NET7_0_OR_GREATER
74+
this.reference = ref MemoryMarshal.GetReference(span);
75+
this.length = span.Length;
76+
#else
6277
this.span = span;
78+
#endif
6379
this.step = step;
6480
}
81+
#endif
6582

6683
/// <summary>
6784
/// Initializes a new instance of the <see cref="ReadOnlyRefEnumerable{T}"/> struct.
@@ -72,8 +89,14 @@ private ReadOnlyRefEnumerable(ReadOnlySpan<T> span, int step)
7289
[MethodImpl(MethodImplOptions.AggressiveInlining)]
7390
internal ReadOnlyRefEnumerable(in T reference, int length, int step)
7491
{
92+
#if NET7_0_OR_GREATER
93+
this.reference = ref reference;
94+
this.length = length;
95+
this.step = step;
96+
#else
7597
this.span = MemoryMarshal.CreateReadOnlySpan(ref Unsafe.AsRef(reference), length);
7698
this.step = step;
99+
#endif
77100
}
78101

79102
/// <summary>
@@ -124,7 +147,9 @@ internal ReadOnlyRefEnumerable(object? instance, IntPtr offset, int length, int
124147
public int Length
125148
{
126149
[MethodImpl(MethodImplOptions.AggressiveInlining)]
127-
#if NETSTANDARD2_1_OR_GREATER
150+
#if NET7_0_OR_GREATER
151+
get => this.length;
152+
#elif NETSTANDARD2_1_OR_GREATER
128153
get => this.span.Length;
129154
#else
130155
get => this.length;
@@ -149,7 +174,9 @@ public ref readonly T this[int index]
149174
ThrowHelper.ThrowIndexOutOfRangeException();
150175
}
151176

152-
#if NETSTANDARD2_1_OR_GREATER
177+
#if NET7_0_OR_GREATER
178+
ref T r0 = ref Unsafe.AsRef(in this.reference);
179+
#elif NETSTANDARD2_1_OR_GREATER
153180
ref T r0 = ref MemoryMarshal.GetReference(this.span);
154181
#else
155182
ref T r0 = ref RuntimeHelpers.GetObjectDataAtOffsetOrPointerReference<T>(this.instance, this.offset);
@@ -181,7 +208,9 @@ public ref readonly T this[Index index]
181208
[MethodImpl(MethodImplOptions.AggressiveInlining)]
182209
public Enumerator GetEnumerator()
183210
{
184-
#if NETSTANDARD2_1_OR_GREATER
211+
#if NET7_0_OR_GREATER
212+
return new(in this.reference, this.length, this.step);
213+
#elif NETSTANDARD2_1_OR_GREATER
185214
return new(this.span, this.step);
186215
#else
187216
return new(this.instance, this.offset, this.length, this.step);
@@ -197,7 +226,26 @@ public Enumerator GetEnumerator()
197226
/// </exception>
198227
public void CopyTo(RefEnumerable<T> destination)
199228
{
200-
#if NETSTANDARD2_1_OR_GREATER
229+
#if NET7_0_OR_GREATER
230+
if (this.step == 1)
231+
{
232+
destination.CopyFrom(MemoryMarshal.CreateReadOnlySpan(ref Unsafe.AsRef(in this.reference), this.length));
233+
234+
return;
235+
}
236+
237+
if (destination.Step == 1)
238+
{
239+
CopyTo(MemoryMarshal.CreateSpan(ref destination.Reference, destination.Length));
240+
241+
return;
242+
}
243+
244+
ref T sourceRef = ref Unsafe.AsRef(in this.reference);
245+
ref T destinationRef = ref destination.Reference;
246+
int sourceLength = this.length;
247+
int destinationLength = destination.Length;
248+
#elif NETSTANDARD2_1_OR_GREATER
201249
if (this.step == 1)
202250
{
203251
destination.CopyFrom(this.span);
@@ -238,7 +286,10 @@ public void CopyTo(RefEnumerable<T> destination)
238286
/// <returns>Whether or not the operation was successful.</returns>
239287
public bool TryCopyTo(RefEnumerable<T> destination)
240288
{
241-
#if NETSTANDARD2_1_OR_GREATER
289+
#if NET7_0_OR_GREATER
290+
int sourceLength = this.length;
291+
int destinationLength = destination.Length;
292+
#elif NETSTANDARD2_1_OR_GREATER
242293
int sourceLength = this.span.Length;
243294
int destinationLength = destination.Span.Length;
244295
#else
@@ -265,7 +316,17 @@ public bool TryCopyTo(RefEnumerable<T> destination)
265316
/// </exception>
266317
public void CopyTo(Span<T> destination)
267318
{
268-
#if NETSTANDARD2_1_OR_GREATER
319+
#if NET7_0_OR_GREATER
320+
if (this.step == 1)
321+
{
322+
MemoryMarshal.CreateReadOnlySpan(ref Unsafe.AsRef(in this.reference), this.length).CopyTo(destination);
323+
324+
return;
325+
}
326+
327+
ref T sourceRef = ref Unsafe.AsRef(in this.reference);
328+
int length = this.length;
329+
#elif NETSTANDARD2_1_OR_GREATER
269330
if (this.step == 1)
270331
{
271332
this.span.CopyTo(destination);
@@ -296,7 +357,9 @@ public void CopyTo(Span<T> destination)
296357
/// <returns>Whether or not the operation was successful.</returns>
297358
public bool TryCopyTo(Span<T> destination)
298359
{
299-
#if NETSTANDARD2_1_OR_GREATER
360+
#if NET7_0_OR_GREATER
361+
int length = this.length;
362+
#elif NETSTANDARD2_1_OR_GREATER
300363
int length = this.span.Length;
301364
#else
302365
int length = this.length;
@@ -315,7 +378,9 @@ public bool TryCopyTo(Span<T> destination)
315378
/// <inheritdoc cref="RefEnumerable{T}.ToArray"/>
316379
public T[] ToArray()
317380
{
318-
#if NETSTANDARD2_1_OR_GREATER
381+
#if NET7_0_OR_GREATER
382+
int length = this.length;
383+
#elif NETSTANDARD2_1_OR_GREATER
319384
int length = this.span.Length;
320385
#else
321386
int length = this.length;
@@ -341,7 +406,9 @@ public T[] ToArray()
341406
[MethodImpl(MethodImplOptions.AggressiveInlining)]
342407
public static implicit operator ReadOnlyRefEnumerable<T>(RefEnumerable<T> enumerable)
343408
{
344-
#if NETSTANDARD2_1_OR_GREATER
409+
#if NET7_0_OR_GREATER
410+
return new(in enumerable.Reference, enumerable.Length, enumerable.Step);
411+
#elif NETSTANDARD2_1_OR_GREATER
345412
return new(enumerable.Span, enumerable.Step);
346413
#else
347414
return new(enumerable.Instance, enumerable.Offset, enumerable.Length, enumerable.Step);
@@ -353,7 +420,13 @@ public static implicit operator ReadOnlyRefEnumerable<T>(RefEnumerable<T> enumer
353420
/// </summary>
354421
public ref struct Enumerator
355422
{
356-
#if NETSTANDARD2_1_OR_GREATER
423+
#if NET7_0_OR_GREATER
424+
/// <inheritdoc cref="ReadOnlyRefEnumerable{T}.reference"/>
425+
private readonly ref readonly T reference;
426+
427+
/// <inheritdoc cref="ReadOnlyRefEnumerable{T}.length"/>
428+
private readonly int length;
429+
#elif NETSTANDARD2_1_OR_GREATER
357430
/// <inheritdoc cref="ReadOnlyRefEnumerable{T}.span"/>
358431
private readonly ReadOnlySpan<T> span;
359432
#else
@@ -375,7 +448,22 @@ public ref struct Enumerator
375448
/// </summary>
376449
private int position;
377450

378-
#if NETSTANDARD2_1_OR_GREATER
451+
#if NET7_0_OR_GREATER
452+
/// <summary>
453+
/// Initializes a new instance of the <see cref="Enumerator"/> struct.
454+
/// </summary>
455+
/// <param name="reference">The <typeparamref name="T"/> reference to the first item of the sequence.</param>
456+
/// <param name="length">The length of the sequence.</param>
457+
/// <param name="step">The distance between items in the sequence to enumerate.</param>
458+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
459+
internal Enumerator(in T reference, int length, int step)
460+
{
461+
this.reference = ref reference;
462+
this.length = length;
463+
this.step = step;
464+
this.position = -1;
465+
}
466+
#elif NETSTANDARD2_1_OR_GREATER
379467
/// <summary>
380468
/// Initializes a new instance of the <see cref="Enumerator"/> struct.
381469
/// </summary>
@@ -411,7 +499,9 @@ internal Enumerator(object? instance, IntPtr offset, int length, int step)
411499
[MethodImpl(MethodImplOptions.AggressiveInlining)]
412500
public bool MoveNext()
413501
{
414-
#if NETSTANDARD2_1_OR_GREATER
502+
#if NET7_0_OR_GREATER
503+
return ++this.position < this.length;
504+
#elif NETSTANDARD2_1_OR_GREATER
415505
return ++this.position < this.span.Length;
416506
#else
417507
return ++this.position < this.length;
@@ -424,7 +514,9 @@ public readonly ref readonly T Current
424514
[MethodImpl(MethodImplOptions.AggressiveInlining)]
425515
get
426516
{
427-
#if NETSTANDARD2_1_OR_GREATER
517+
#if NET7_0_OR_GREATER
518+
ref T r0 = ref Unsafe.AsRef(in this.reference);
519+
#elif NETSTANDARD2_1_OR_GREATER
428520
ref T r0 = ref this.span.DangerousGetReference();
429521
#else
430522
ref T r0 = ref RuntimeHelpers.GetObjectDataAtOffsetOrPointerReference<T>(this.instance, this.offset);

0 commit comments

Comments
 (0)