@@ -16,10 +16,35 @@ namespace Microsoft.Toolkit.HighPerformance.Enumerables
1616 /// </summary>
1717 /// <typeparam name="T">The type of items to enumerate.</typeparam>
1818 [ EditorBrowsable ( EditorBrowsableState . Never ) ]
19- public readonly ref struct Array2DColumnEnumerable < T >
19+ public ref struct Array2DColumnEnumerable < T >
2020 {
21+ #if SPAN_RUNTIME_SUPPORT
22+ /// <summary>
23+ /// The <see cref="Span{T}"/> instance mapping the target 2D array.
24+ /// </summary>
25+ /// <remarks>
26+ /// In runtimes where we have support for the <see cref="Span{T}"/> type, we can
27+ /// create one from the input 2D array and use that to traverse the target column.
28+ /// This reduces the number of operations to perform for the offsetting to the right
29+ /// column element (we simply need to add <see cref="width"/> to the offset at each
30+ /// iteration to move down by one row), and allows us to use the fast <see cref="Span{T}"/>
31+ /// accessor instead of the slower indexer for 2D arrays, as we can then access each
32+ /// individual item linearly, since we know the absolute offset from the base location.
33+ /// </remarks>
34+ private readonly Span < T > span ;
35+
36+ /// <summary>
37+ /// The width of the target 2D array.
38+ /// </summary>
39+ private readonly int width ;
40+
41+ /// <summary>
42+ /// The current absolute offset within <see cref="span"/>.
43+ /// </summary>
44+ private int offset ;
45+ #else
2146 /// <summary>
22- /// The source 2D <typeparamref name="T"/> array instance.
47+ /// The source 2D array instance.
2348 /// </summary>
2449 private readonly T [ , ] array ;
2550
@@ -28,24 +53,93 @@ public readonly ref struct Array2DColumnEnumerable<T>
2853 /// </summary>
2954 private readonly int column ;
3055
56+ /// <summary>
57+ /// The height of a column in <see cref="array"/>.
58+ /// </summary>
59+ private readonly int height ;
60+
61+ /// <summary>
62+ /// The current row.
63+ /// </summary>
64+ private int row ;
65+ #endif
66+
3167 /// <summary>
3268 /// Initializes a new instance of the <see cref="Array2DColumnEnumerable{T}"/> struct.
3369 /// </summary>
34- /// <param name="array">The source 2D <typeparamref name="T"/> array instance.</param>
70+ /// <param name="array">The source 2D array instance.</param>
3571 /// <param name="column">The target column to iterate within <paramref name="array"/>.</param>
3672 [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
3773 public Array2DColumnEnumerable ( T [ , ] array , int column )
3874 {
75+ if ( ( uint ) column >= ( uint ) array . GetLength ( 1 ) )
76+ {
77+ ThrowArgumentOutOfRangeExceptionForInvalidColumn ( ) ;
78+ }
79+
80+ #if SPAN_RUNTIME_SUPPORT
81+ this . span = array . AsSpan ( ) ;
82+ this . width = array . GetLength ( 1 ) ;
83+ this . offset = column - this . width ;
84+ #else
3985 this . array = array ;
4086 this . column = column ;
87+ this . height = array . GetLength ( 0 ) ;
88+ this . row = - 1 ;
89+ #endif
4190 }
4291
4392 /// <summary>
4493 /// Implements the duck-typed <see cref="IEnumerable{T}.GetEnumerator"/> method.
4594 /// </summary>
46- /// <returns>An <see cref="Enumerator "/> instance targeting the current 2D <typeparamref name="T"/> array instance.</returns>
95+ /// <returns>An <see cref="Array2DColumnEnumerable{T} "/> instance targeting the current 2D <typeparamref name="T"/> array instance.</returns>
4796 [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
48- public Enumerator GetEnumerator ( ) => new Enumerator ( this . array , this . column ) ;
97+ public Array2DColumnEnumerable < T > GetEnumerator ( ) => this ;
98+
99+ /// <summary>
100+ /// Implements the duck-typed <see cref="System.Collections.IEnumerator.MoveNext"/> method.
101+ /// </summary>
102+ /// <returns><see langword="true"/> whether a new element is available, <see langword="false"/> otherwise</returns>
103+ [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
104+ public bool MoveNext ( )
105+ {
106+ #if SPAN_RUNTIME_SUPPORT
107+ int offset = this . offset + this . width ;
108+
109+ if ( ( uint ) offset < ( uint ) this . span . Length )
110+ {
111+ this . offset = offset ;
112+
113+ return true ;
114+ }
115+ #else
116+ int row = this . row + 1 ;
117+
118+ if ( row < this . height )
119+ {
120+ this . row = row ;
121+
122+ return true ;
123+ }
124+ #endif
125+ return false ;
126+ }
127+
128+ /// <summary>
129+ /// Gets the duck-typed <see cref="IEnumerator{T}.Current"/> property.
130+ /// </summary>
131+ public ref T Current
132+ {
133+ [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
134+ get
135+ {
136+ #if SPAN_RUNTIME_SUPPORT
137+ return ref this . span . DangerousGetReferenceAt ( this . offset ) ;
138+ #else
139+ return ref this . array [ this . row , this . column ] ;
140+ #endif
141+ }
142+ }
49143
50144 /// <summary>
51145 /// Returns a <typeparamref name="T"/> array with the values in the target column.
@@ -79,129 +173,6 @@ public T[] ToArray()
79173 return array ;
80174 }
81175
82- /// <summary>
83- /// An enumerator for a source 2D array instance.
84- /// </summary>
85- [ EditorBrowsable ( EditorBrowsableState . Never ) ]
86- public ref struct Enumerator
87- {
88- #if SPAN_RUNTIME_SUPPORT
89- /// <summary>
90- /// The <see cref="Span{T}"/> instance mapping the target 2D array.
91- /// </summary>
92- /// <remarks>
93- /// In runtimes where we have support for the <see cref="Span{T}"/> type, we can
94- /// create one from the input 2D array and use that to traverse the target column.
95- /// This reduces the number of operations to perform for the offsetting to the right
96- /// column element (we simply need to add <see cref="width"/> to the offset at each
97- /// iteration to move down by one row), and allows us to use the fast <see cref="Span{T}"/>
98- /// accessor instead of the slower indexer for 2D arrays, as we can then access each
99- /// individual item linearly, since we know the absolute offset from the base location.
100- /// </remarks>
101- private readonly Span < T > span ;
102-
103- /// <summary>
104- /// The width of the target 2D array.
105- /// </summary>
106- private readonly int width ;
107-
108- /// <summary>
109- /// The current absolute offset within <see cref="span"/>.
110- /// </summary>
111- private int offset ;
112- #else
113- /// <summary>
114- /// The source 2D array instance.
115- /// </summary>
116- private readonly T [ , ] array ;
117-
118- /// <summary>
119- /// The target column to iterate within <see cref="array"/>.
120- /// </summary>
121- private readonly int column ;
122-
123- /// <summary>
124- /// The height of a column in <see cref="array"/>.
125- /// </summary>
126- private readonly int height ;
127-
128- /// <summary>
129- /// The current row.
130- /// </summary>
131- private int row ;
132- #endif
133-
134- /// <summary>
135- /// Initializes a new instance of the <see cref="Enumerator"/> struct.
136- /// </summary>
137- /// <param name="array">The source 2D array instance.</param>
138- /// <param name="column">The target column to iterate within <paramref name="array"/>.</param>
139- [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
140- public Enumerator ( T [ , ] array , int column )
141- {
142- if ( ( uint ) column >= ( uint ) array . GetLength ( 1 ) )
143- {
144- ThrowArgumentOutOfRangeExceptionForInvalidColumn ( ) ;
145- }
146-
147- #if SPAN_RUNTIME_SUPPORT
148- this . span = array . AsSpan ( ) ;
149- this . width = array . GetLength ( 1 ) ;
150- this . offset = column - this . width ;
151- #else
152- this . array = array ;
153- this . column = column ;
154- this . height = array . GetLength ( 0 ) ;
155- this . row = - 1 ;
156- #endif
157- }
158-
159- /// <summary>
160- /// Implements the duck-typed <see cref="System.Collections.IEnumerator.MoveNext"/> method.
161- /// </summary>
162- /// <returns><see langword="true"/> whether a new element is available, <see langword="false"/> otherwise</returns>
163- [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
164- public bool MoveNext ( )
165- {
166- #if SPAN_RUNTIME_SUPPORT
167- int offset = this . offset + this . width ;
168-
169- if ( ( uint ) offset < ( uint ) this . span . Length )
170- {
171- this . offset = offset ;
172-
173- return true ;
174- }
175- #else
176- int row = this . row + 1 ;
177-
178- if ( row < this . height )
179- {
180- this . row = row ;
181-
182- return true ;
183- }
184- #endif
185- return false ;
186- }
187-
188- /// <summary>
189- /// Gets the duck-typed <see cref="IEnumerator{T}.Current"/> property.
190- /// </summary>
191- public ref T Current
192- {
193- [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
194- get
195- {
196- #if SPAN_RUNTIME_SUPPORT
197- return ref this . span . DangerousGetReferenceAt ( this . offset ) ;
198- #else
199- return ref this . array [ this . row , this . column ] ;
200- #endif
201- }
202- }
203- }
204-
205176 /// <summary>
206177 /// Throws an <see cref="ArgumentOutOfRangeException"/> when the <see cref="column"/> is invalid.
207178 /// </summary>
0 commit comments