Skip to content

Commit 86625a0

Browse files
author
DaZombieKiller
committed
Rework public length and indexer properties for RefEnumerable<T>
1 parent 0152072 commit 86625a0

File tree

2 files changed

+175
-174
lines changed

2 files changed

+175
-174
lines changed

Microsoft.Toolkit.HighPerformance/Enumerables/ReadOnlyRefEnumerable{T}.cs

Lines changed: 88 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,6 @@ public readonly ref struct ReadOnlyRefEnumerable<T>
2929
/// </summary>
3030
/// <remarks>The <see cref="ReadOnlySpan{T}.Length"/> field maps to the total available length.</remarks>
3131
private readonly ReadOnlySpan<T> span;
32-
33-
/// <summary>
34-
/// Gets the total available length for the sequence.
35-
/// </summary>
36-
public int Length => span.Length;
3732
#else
3833
/// <summary>
3934
/// The target <see cref="object"/> instance, if present.
@@ -46,9 +41,9 @@ public readonly ref struct ReadOnlyRefEnumerable<T>
4641
private readonly IntPtr offset;
4742

4843
/// <summary>
49-
/// Gets the total available length for the sequence.
44+
/// The total available length for the sequence.
5045
/// </summary>
51-
public int Length { get; }
46+
private readonly int length;
5247
#endif
5348

5449
/// <summary>
@@ -57,40 +52,6 @@ public readonly ref struct ReadOnlyRefEnumerable<T>
5752
/// <remarks>The distance refers to <typeparamref name="T"/> items, not byte offset.</remarks>
5853
private readonly int step;
5954

60-
/// <summary>
61-
/// Gets the element at the specified zero-based index.
62-
/// </summary>
63-
/// <returns>A reference to the element at the specified index.</returns>
64-
/// <exception cref="IndexOutOfRangeException">
65-
/// Thrown when <paramref name="index"/> is invalid.
66-
/// </exception>
67-
public ref readonly T this[int index]
68-
{
69-
get
70-
{
71-
if ((uint)index >= (uint)Length)
72-
{
73-
ThrowHelper.ThrowIndexOutOfRangeException();
74-
}
75-
76-
return ref DangerousGetReferenceAt(index);
77-
}
78-
}
79-
80-
#if NETSTANDARD2_1_OR_GREATER
81-
/// <summary>
82-
/// Gets the element at the specified zero-based index.
83-
/// </summary>
84-
/// <returns>A reference to the element at the specified index.</returns>
85-
/// <exception cref="IndexOutOfRangeException">
86-
/// Thrown when <paramref name="index"/> is invalid.
87-
/// </exception>
88-
public ref readonly T this[Index index]
89-
{
90-
get => ref this[index.GetOffset(Length)];
91-
}
92-
#endif
93-
9455
#if SPAN_RUNTIME_SUPPORT
9556
/// <summary>
9657
/// Initializes a new instance of the <see cref="ReadOnlyRefEnumerable{T}"/> struct.
@@ -155,53 +116,76 @@ internal ReadOnlyRefEnumerable(object? instance, IntPtr offset, int length, int
155116
{
156117
this.instance = instance;
157118
this.offset = offset;
158-
Length = length;
119+
this.length = length;
159120
this.step = step;
160121
}
161122
#endif
162123

163124
/// <summary>
164-
/// Returns a reference to the first element within the current instance, with no bounds check.
125+
/// Gets the total available length for the sequence.
165126
/// </summary>
166-
/// <returns>A reference to the first element within the current instance.</returns>
167-
internal ref readonly T DangerousGetReference()
127+
public int Length
168128
{
169129
#if SPAN_RUNTIME_SUPPORT
170-
return ref MemoryMarshal.GetReference(this.span);
130+
get => this.span.Length;
171131
#else
172-
return ref RuntimeHelpers.GetObjectDataAtOffsetOrPointerReference<T>(this.instance, this.offset);
132+
get => this.length;
173133
#endif
174134
}
175135

176136
/// <summary>
177-
/// Returns a reference to a specified element within the current instance, with no bounds check.
137+
/// Gets the element at the specified zero-based index.
178138
/// </summary>
179139
/// <returns>A reference to the element at the specified index.</returns>
180-
internal ref readonly T DangerousGetReferenceAt(int index)
140+
/// <exception cref="IndexOutOfRangeException">
141+
/// Thrown when <paramref name="index"/> is invalid.
142+
/// </exception>
143+
public ref readonly T this[int index]
181144
{
145+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
146+
get
147+
{
148+
if ((uint)index >= (uint)Length)
149+
{
150+
ThrowHelper.ThrowIndexOutOfRangeException();
151+
}
152+
182153
#if SPAN_RUNTIME_SUPPORT
183-
ref T r0 = ref MemoryMarshal.GetReference(this.span);
154+
ref T r0 = ref MemoryMarshal.GetReference(this.span);
184155
#else
185-
ref T r0 = ref RuntimeHelpers.GetObjectDataAtOffsetOrPointerReference<T>(this.instance, this.offset);
156+
ref T r0 = ref RuntimeHelpers.GetObjectDataAtOffsetOrPointerReference<T>(this.instance, this.offset);
186157
#endif
158+
nint offset = (nint)(uint)index * (nint)(uint)this.step;
159+
ref T ri = ref Unsafe.Add(ref r0, offset);
160+
return ref ri;
161+
}
162+
}
187163

188-
// Here we just offset by shifting down as if we were traversing a 2D array with a
189-
// a single column, with the width of each row represented by the step, the height
190-
// represented by the current position, and with only the first element of each row
191-
// being inspected. We can perform all the indexing operations in this type as nint,
192-
// as the maximum offset is guaranteed never to exceed the maximum value, since on
193-
// 32 bit architectures it's not possible to allocate that much memory anyway.
194-
nint offset = (nint)(uint)index * (nint)(uint)this.step;
195-
ref T ri = ref Unsafe.Add(ref r0, offset);
196-
return ref ri;
164+
#if NETSTANDARD2_1_OR_GREATER
165+
/// <summary>
166+
/// Gets the element at the specified zero-based index.
167+
/// </summary>
168+
/// <returns>A reference to the element at the specified index.</returns>
169+
/// <exception cref="IndexOutOfRangeException">
170+
/// Thrown when <paramref name="index"/> is invalid.
171+
/// </exception>
172+
public ref readonly T this[Index index]
173+
{
174+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
175+
get => ref this[index.GetOffset(Length)];
197176
}
177+
#endif
198178

199179
/// <inheritdoc cref="System.Collections.IEnumerable.GetEnumerator"/>
200180
[Pure]
201181
[MethodImpl(MethodImplOptions.AggressiveInlining)]
202182
public Enumerator GetEnumerator()
203183
{
204-
return new Enumerator(this);
184+
#if SPAN_RUNTIME_SUPPORT
185+
return new Enumerator(this.span, this.step);
186+
#else
187+
return new Enumerator(this.instance, this.offset, this.length, this.step);
188+
#endif
205189
}
206190

207191
/// <summary>
@@ -237,7 +221,7 @@ public void CopyTo(RefEnumerable<T> destination)
237221
ref T sourceRef = ref RuntimeHelpers.GetObjectDataAtOffsetOrPointerReference<T>(this.instance, this.offset);
238222
ref T destinationRef = ref RuntimeHelpers.GetObjectDataAtOffsetOrPointerReference<T>(destination.Instance, destination.Offset);
239223
int
240-
sourceLength = Length,
224+
sourceLength = this.length,
241225
destinationLength = destination.Length;
242226
#endif
243227

@@ -262,7 +246,7 @@ public bool TryCopyTo(RefEnumerable<T> destination)
262246
destinationLength = destination.Span.Length;
263247
#else
264248
int
265-
sourceLength = Length,
249+
sourceLength = this.length,
266250
destinationLength = destination.Length;
267251
#endif
268252

@@ -297,7 +281,7 @@ public void CopyTo(Span<T> destination)
297281
int length = this.span.Length;
298282
#else
299283
ref T sourceRef = ref RuntimeHelpers.GetObjectDataAtOffsetOrPointerReference<T>(this.instance, this.offset);
300-
int length = Length;
284+
int length = this.length;
301285
#endif
302286
if ((uint)destination.Length < (uint)length)
303287
{
@@ -319,7 +303,7 @@ public bool TryCopyTo(Span<T> destination)
319303
#if SPAN_RUNTIME_SUPPORT
320304
int length = this.span.Length;
321305
#else
322-
int length = Length;
306+
int length = this.length;
323307
#endif
324308

325309
if (destination.Length >= length)
@@ -339,7 +323,7 @@ public T[] ToArray()
339323
#if SPAN_RUNTIME_SUPPORT
340324
int length = this.span.Length;
341325
#else
342-
int length = Length;
326+
int length = this.length;
343327
#endif
344328

345329
// Empty array if no data is mapped
@@ -374,10 +358,22 @@ public static implicit operator ReadOnlyRefEnumerable<T>(RefEnumerable<T> enumer
374358
/// </summary>
375359
public ref struct Enumerator
376360
{
377-
/// <summary>
378-
/// The <see cref="ReadOnlyRefEnumerable{T}"/> used by this enumerator.
379-
/// </summary>
380-
private readonly ReadOnlyRefEnumerable<T> enumerable;
361+
#if SPAN_RUNTIME_SUPPORT
362+
/// <inheritdoc cref="ReadOnlyRefEnumerable{T}.span"/>
363+
private readonly ReadOnlySpan<T> span;
364+
#else
365+
/// <inheritdoc cref="ReadOnlyRefEnumerable{T}.instance"/>
366+
private readonly object? instance;
367+
368+
/// <inheritdoc cref="ReadOnlyRefEnumerable{T}.offset"/>
369+
private readonly IntPtr offset;
370+
371+
/// <inheritdoc cref="ReadOnlyRefEnumerable{T}.length"/>
372+
private readonly int length;
373+
#endif
374+
375+
/// <inheritdoc cref="ReadOnlyRefEnumerable{T}.step"/>
376+
private readonly int step;
381377

382378
/// <summary>
383379
/// The current position in the sequence.
@@ -392,8 +388,10 @@ public ref struct Enumerator
392388
/// <param name="step">The distance between items in the sequence to enumerate.</param>
393389
[MethodImpl(MethodImplOptions.AggressiveInlining)]
394390
internal Enumerator(ReadOnlySpan<T> span, int step)
395-
: this(new ReadOnlyRefEnumerable<T>(span, step))
396391
{
392+
this.span = span;
393+
this.step = step;
394+
this.position = -1;
397395
}
398396
#else
399397
/// <summary>
@@ -405,33 +403,42 @@ internal Enumerator(ReadOnlySpan<T> span, int step)
405403
/// <param name="step">The distance between items in the sequence to enumerate.</param>
406404
[MethodImpl(MethodImplOptions.AggressiveInlining)]
407405
internal Enumerator(object? instance, IntPtr offset, int length, int step)
408-
: this(new ReadOnlyRefEnumerable<T>(instance, offset, length, step))
409406
{
410-
}
411-
#endif
412-
413-
internal Enumerator(ReadOnlyRefEnumerable<T> enumerable)
414-
{
415-
this.enumerable = enumerable;
407+
this.instance = instance;
408+
this.offset = offset;
409+
this.length = length;
410+
this.step = step;
416411
this.position = -1;
417412
}
413+
#endif
418414

419415
/// <inheritdoc cref="System.Collections.IEnumerator.MoveNext"/>
420416
[MethodImpl(MethodImplOptions.AggressiveInlining)]
421417
public bool MoveNext()
422418
{
423419
#if SPAN_RUNTIME_SUPPORT
424-
return ++this.position < this.enumerable.span.Length;
420+
return ++this.position < this.span.Length;
425421
#else
426-
return ++this.position < this.enumerable.Length;
422+
return ++this.position < this.length;
427423
#endif
428424
}
429425

430426
/// <inheritdoc cref="System.Collections.Generic.IEnumerator{T}.Current"/>
431427
public readonly ref readonly T Current
432428
{
433429
[MethodImpl(MethodImplOptions.AggressiveInlining)]
434-
get => ref this.enumerable.DangerousGetReferenceAt(this.position);
430+
get
431+
{
432+
#if SPAN_RUNTIME_SUPPORT
433+
ref T r0 = ref this.span.DangerousGetReference();
434+
#else
435+
ref T r0 = ref RuntimeHelpers.GetObjectDataAtOffsetOrPointerReference<T>(this.instance, this.offset);
436+
#endif
437+
nint offset = (nint)(uint)this.position * (nint)(uint)this.step;
438+
ref T ri = ref Unsafe.Add(ref r0, offset);
439+
440+
return ref ri;
441+
}
435442
}
436443
}
437444

0 commit comments

Comments
 (0)