@@ -29,6 +29,11 @@ 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 ;
3237#else
3338 /// <summary>
3439 /// The target <see cref="object"/> instance, if present.
@@ -41,9 +46,9 @@ public readonly ref struct ReadOnlyRefEnumerable<T>
4146 private readonly IntPtr offset ;
4247
4348 /// <summary>
44- /// The total available length for the sequence.
49+ /// Gets the total available length for the sequence.
4550 /// </summary>
46- private readonly int length ;
51+ public int Length { get ; }
4752#endif
4853
4954 /// <summary>
@@ -52,6 +57,40 @@ public readonly ref struct ReadOnlyRefEnumerable<T>
5257 /// <remarks>The distance refers to <typeparamref name="T"/> items, not byte offset.</remarks>
5358 private readonly int step ;
5459
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+
5594#if SPAN_RUNTIME_SUPPORT
5695 /// <summary>
5796 /// Initializes a new instance of the <see cref="ReadOnlyRefEnumerable{T}"/> struct.
@@ -116,21 +155,52 @@ internal ReadOnlyRefEnumerable(object? instance, IntPtr offset, int length, int
116155 {
117156 this . instance = instance ;
118157 this . offset = offset ;
119- this . length = length ;
158+ Length = length ;
120159 this . step = step ;
121160 }
122161#endif
123162
163+ /// <summary>
164+ /// Returns a reference to the first element within the current instance, with no bounds check.
165+ /// </summary>
166+ /// <returns>A reference to the first element within the current instance.</returns>
167+ internal ref readonly T DangerousGetReference ( )
168+ {
169+ #if SPAN_RUNTIME_SUPPORT
170+ return ref MemoryMarshal . GetReference ( this . span ) ;
171+ #else
172+ return ref RuntimeHelpers . GetObjectDataAtOffsetOrPointerReference < T > ( this . instance , this . offset ) ;
173+ #endif
174+ }
175+
176+ /// <summary>
177+ /// Returns a reference to a specified element within the current instance, with no bounds check.
178+ /// </summary>
179+ /// <returns>A reference to the element at the specified index.</returns>
180+ internal ref readonly T DangerousGetReferenceAt ( int index )
181+ {
182+ #if SPAN_RUNTIME_SUPPORT
183+ ref T r0 = ref MemoryMarshal . GetReference ( this . span ) ;
184+ #else
185+ ref T r0 = ref RuntimeHelpers . GetObjectDataAtOffsetOrPointerReference < T > ( this . instance , this . offset ) ;
186+ #endif
187+ // Here we just offset by shifting down as if we were traversing a 2D array with a
188+ // a single column, with the width of each row represented by the step, the height
189+ // represented by the current position, and with only the first element of each row
190+ // being inspected. We can perform all the indexing operations in this type as nint,
191+ // as the maximum offset is guaranteed never to exceed the maximum value, since on
192+ // 32 bit architectures it's not possible to allocate that much memory anyway.
193+ nint offset = ( nint ) ( uint ) index * ( nint ) ( uint ) this . step ;
194+ ref T ri = ref Unsafe . Add ( ref r0 , offset ) ;
195+ return ref ri ;
196+ }
197+
124198 /// <inheritdoc cref="System.Collections.IEnumerable.GetEnumerator"/>
125199 [ Pure ]
126200 [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
127201 public Enumerator GetEnumerator ( )
128202 {
129- #if SPAN_RUNTIME_SUPPORT
130- return new Enumerator ( this . span , this . step ) ;
131- #else
132- return new Enumerator ( this . instance , this . offset , this . length , this . step ) ;
133- #endif
203+ return new Enumerator ( this ) ;
134204 }
135205
136206 /// <summary>
@@ -166,7 +236,7 @@ public void CopyTo(RefEnumerable<T> destination)
166236 ref T sourceRef = ref RuntimeHelpers . GetObjectDataAtOffsetOrPointerReference < T > ( this . instance , this . offset ) ;
167237 ref T destinationRef = ref RuntimeHelpers . GetObjectDataAtOffsetOrPointerReference < T > ( destination . Instance , destination . Offset ) ;
168238 int
169- sourceLength = this . length ,
239+ sourceLength = Length ,
170240 destinationLength = destination . Length ;
171241#endif
172242
@@ -191,7 +261,7 @@ public bool TryCopyTo(RefEnumerable<T> destination)
191261 destinationLength = destination . Span . Length ;
192262#else
193263 int
194- sourceLength = this . length ,
264+ sourceLength = Length ,
195265 destinationLength = destination . Length ;
196266#endif
197267
@@ -226,7 +296,7 @@ public void CopyTo(Span<T> destination)
226296 int length = this . span . Length ;
227297#else
228298 ref T sourceRef = ref RuntimeHelpers . GetObjectDataAtOffsetOrPointerReference < T > ( this . instance , this . offset ) ;
229- int length = this . length ;
299+ int length = Length ;
230300#endif
231301 if ( ( uint ) destination . Length < ( uint ) length )
232302 {
@@ -248,7 +318,7 @@ public bool TryCopyTo(Span<T> destination)
248318#if SPAN_RUNTIME_SUPPORT
249319 int length = this . span . Length ;
250320#else
251- int length = this . length ;
321+ int length = Length ;
252322#endif
253323
254324 if ( destination . Length >= length )
@@ -268,7 +338,7 @@ public T[] ToArray()
268338#if SPAN_RUNTIME_SUPPORT
269339 int length = this . span . Length ;
270340#else
271- int length = this . length ;
341+ int length = Length ;
272342#endif
273343
274344 // Empty array if no data is mapped
@@ -303,22 +373,10 @@ public static implicit operator ReadOnlyRefEnumerable<T>(RefEnumerable<T> enumer
303373 /// </summary>
304374 public ref struct Enumerator
305375 {
306- #if SPAN_RUNTIME_SUPPORT
307- /// <inheritdoc cref="ReadOnlyRefEnumerable{T}.span"/>
308- private readonly ReadOnlySpan < T > span ;
309- #else
310- /// <inheritdoc cref="ReadOnlyRefEnumerable{T}.instance"/>
311- private readonly object ? instance ;
312-
313- /// <inheritdoc cref="ReadOnlyRefEnumerable{T}.offset"/>
314- private readonly IntPtr offset ;
315-
316- /// <inheritdoc cref="ReadOnlyRefEnumerable{T}.length"/>
317- private readonly int length ;
318- #endif
319-
320- /// <inheritdoc cref="ReadOnlyRefEnumerable{T}.step"/>
321- private readonly int step ;
376+ /// <summary>
377+ /// The <see cref="ReadOnlyRefEnumerable{T}"/> used by this enumerator.
378+ /// </summary>
379+ private readonly ReadOnlyRefEnumerable < T > enumerable ;
322380
323381 /// <summary>
324382 /// The current position in the sequence.
@@ -333,10 +391,8 @@ public ref struct Enumerator
333391 /// <param name="step">The distance between items in the sequence to enumerate.</param>
334392 [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
335393 internal Enumerator ( ReadOnlySpan < T > span , int step )
394+ : this ( new ReadOnlyRefEnumerable < T > ( span , step ) )
336395 {
337- this . span = span ;
338- this . step = step ;
339- this . position = - 1 ;
340396 }
341397#else
342398 /// <summary>
@@ -348,42 +404,33 @@ internal Enumerator(ReadOnlySpan<T> span, int step)
348404 /// <param name="step">The distance between items in the sequence to enumerate.</param>
349405 [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
350406 internal Enumerator ( object ? instance , IntPtr offset , int length , int step )
407+ : this ( new ReadOnlyRefEnumerable < T > ( instance , offset , length , step ) )
351408 {
352- this . instance = instance ;
353- this . offset = offset ;
354- this . length = length ;
355- this . step = step ;
356- this . position = - 1 ;
357409 }
358410#endif
359411
412+ internal Enumerator ( ReadOnlyRefEnumerable < T > enumerable )
413+ {
414+ this . enumerable = enumerable ;
415+ this . position = - 1 ;
416+ }
417+
360418 /// <inheritdoc cref="System.Collections.IEnumerator.MoveNext"/>
361419 [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
362420 public bool MoveNext ( )
363421 {
364422#if SPAN_RUNTIME_SUPPORT
365- return ++ this . position < this . span . Length ;
423+ return ++ this . position < this . enumerable . span . Length ;
366424#else
367- return ++ this . position < this . length ;
425+ return ++ this . position < this . enumerable . Length ;
368426#endif
369427 }
370428
371429 /// <inheritdoc cref="System.Collections.Generic.IEnumerator{T}.Current"/>
372430 public readonly ref readonly T Current
373431 {
374432 [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
375- get
376- {
377- #if SPAN_RUNTIME_SUPPORT
378- ref T r0 = ref this . span . DangerousGetReference ( ) ;
379- #else
380- ref T r0 = ref RuntimeHelpers . GetObjectDataAtOffsetOrPointerReference < T > ( this . instance , this . offset ) ;
381- #endif
382- nint offset = ( nint ) ( uint ) this . position * ( nint ) ( uint ) this . step ;
383- ref T ri = ref Unsafe . Add ( ref r0 , offset ) ;
384-
385- return ref ri ;
386- }
433+ get => ref this . enumerable . DangerousGetReferenceAt ( this . position ) ;
387434 }
388435 }
389436
0 commit comments