@@ -29,6 +29,11 @@ public readonly ref struct ReadOnlyRefEnumerable<T>
29
29
/// </summary>
30
30
/// <remarks>The <see cref="ReadOnlySpan{T}.Length"/> field maps to the total available length.</remarks>
31
31
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 ;
32
37
#else
33
38
/// <summary>
34
39
/// The target <see cref="object"/> instance, if present.
@@ -41,9 +46,9 @@ public readonly ref struct ReadOnlyRefEnumerable<T>
41
46
private readonly IntPtr offset ;
42
47
43
48
/// <summary>
44
- /// The total available length for the sequence.
49
+ /// Gets the total available length for the sequence.
45
50
/// </summary>
46
- private readonly int length ;
51
+ public int Length { get ; }
47
52
#endif
48
53
49
54
/// <summary>
@@ -52,6 +57,40 @@ public readonly ref struct ReadOnlyRefEnumerable<T>
52
57
/// <remarks>The distance refers to <typeparamref name="T"/> items, not byte offset.</remarks>
53
58
private readonly int step ;
54
59
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
+
55
94
#if SPAN_RUNTIME_SUPPORT
56
95
/// <summary>
57
96
/// Initializes a new instance of the <see cref="ReadOnlyRefEnumerable{T}"/> struct.
@@ -116,21 +155,52 @@ internal ReadOnlyRefEnumerable(object? instance, IntPtr offset, int length, int
116
155
{
117
156
this . instance = instance ;
118
157
this . offset = offset ;
119
- this . length = length ;
158
+ Length = length ;
120
159
this . step = step ;
121
160
}
122
161
#endif
123
162
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
+
124
198
/// <inheritdoc cref="System.Collections.IEnumerable.GetEnumerator"/>
125
199
[ Pure ]
126
200
[ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
127
201
public Enumerator GetEnumerator ( )
128
202
{
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 ) ;
134
204
}
135
205
136
206
/// <summary>
@@ -166,7 +236,7 @@ public void CopyTo(RefEnumerable<T> destination)
166
236
ref T sourceRef = ref RuntimeHelpers . GetObjectDataAtOffsetOrPointerReference < T > ( this . instance , this . offset ) ;
167
237
ref T destinationRef = ref RuntimeHelpers . GetObjectDataAtOffsetOrPointerReference < T > ( destination . Instance , destination . Offset ) ;
168
238
int
169
- sourceLength = this . length ,
239
+ sourceLength = Length ,
170
240
destinationLength = destination . Length ;
171
241
#endif
172
242
@@ -191,7 +261,7 @@ public bool TryCopyTo(RefEnumerable<T> destination)
191
261
destinationLength = destination . Span . Length ;
192
262
#else
193
263
int
194
- sourceLength = this . length ,
264
+ sourceLength = Length ,
195
265
destinationLength = destination . Length ;
196
266
#endif
197
267
@@ -226,7 +296,7 @@ public void CopyTo(Span<T> destination)
226
296
int length = this . span . Length ;
227
297
#else
228
298
ref T sourceRef = ref RuntimeHelpers . GetObjectDataAtOffsetOrPointerReference < T > ( this . instance , this . offset ) ;
229
- int length = this . length ;
299
+ int length = Length ;
230
300
#endif
231
301
if ( ( uint ) destination . Length < ( uint ) length )
232
302
{
@@ -248,7 +318,7 @@ public bool TryCopyTo(Span<T> destination)
248
318
#if SPAN_RUNTIME_SUPPORT
249
319
int length = this . span . Length ;
250
320
#else
251
- int length = this . length ;
321
+ int length = Length ;
252
322
#endif
253
323
254
324
if ( destination . Length >= length )
@@ -268,7 +338,7 @@ public T[] ToArray()
268
338
#if SPAN_RUNTIME_SUPPORT
269
339
int length = this . span . Length ;
270
340
#else
271
- int length = this . length ;
341
+ int length = Length ;
272
342
#endif
273
343
274
344
// Empty array if no data is mapped
@@ -303,22 +373,10 @@ public static implicit operator ReadOnlyRefEnumerable<T>(RefEnumerable<T> enumer
303
373
/// </summary>
304
374
public ref struct Enumerator
305
375
{
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 ;
322
380
323
381
/// <summary>
324
382
/// The current position in the sequence.
@@ -333,10 +391,8 @@ public ref struct Enumerator
333
391
/// <param name="step">The distance between items in the sequence to enumerate.</param>
334
392
[ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
335
393
internal Enumerator ( ReadOnlySpan < T > span , int step )
394
+ : this ( new ReadOnlyRefEnumerable < T > ( span , step ) )
336
395
{
337
- this . span = span ;
338
- this . step = step ;
339
- this . position = - 1 ;
340
396
}
341
397
#else
342
398
/// <summary>
@@ -348,42 +404,33 @@ internal Enumerator(ReadOnlySpan<T> span, int step)
348
404
/// <param name="step">The distance between items in the sequence to enumerate.</param>
349
405
[ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
350
406
internal Enumerator ( object ? instance , IntPtr offset , int length , int step )
407
+ : this ( new ReadOnlyRefEnumerable < T > ( instance , offset , length , step ) )
351
408
{
352
- this . instance = instance ;
353
- this . offset = offset ;
354
- this . length = length ;
355
- this . step = step ;
356
- this . position = - 1 ;
357
409
}
358
410
#endif
359
411
412
+ internal Enumerator ( ReadOnlyRefEnumerable < T > enumerable )
413
+ {
414
+ this . enumerable = enumerable ;
415
+ this . position = - 1 ;
416
+ }
417
+
360
418
/// <inheritdoc cref="System.Collections.IEnumerator.MoveNext"/>
361
419
[ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
362
420
public bool MoveNext ( )
363
421
{
364
422
#if SPAN_RUNTIME_SUPPORT
365
- return ++ this . position < this . span . Length ;
423
+ return ++ this . position < this . enumerable . span . Length ;
366
424
#else
367
- return ++ this . position < this . length ;
425
+ return ++ this . position < this . enumerable . Length ;
368
426
#endif
369
427
}
370
428
371
429
/// <inheritdoc cref="System.Collections.Generic.IEnumerator{T}.Current"/>
372
430
public readonly ref readonly T Current
373
431
{
374
432
[ 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 ) ;
387
434
}
388
435
}
389
436
0 commit comments