@@ -14,32 +14,34 @@ public static partial class Extensions
1414 /// It must be at least the length of the count.
1515 /// </param>
1616 /// <inheritdoc cref="Subsets{T}(IReadOnlyList{T}, int)"/>
17- public static IEnumerable < T [ ] > Subsets < T > ( this IReadOnlyList < T > source , int count , T [ ] buffer )
17+ public static IEnumerable < Memory < T > > Subsets < T > ( this IReadOnlyList < T > source , int count , Memory < T > buffer )
1818 {
19+ if ( source is null )
20+ throw new ArgumentNullException ( nameof ( source ) ) ;
1921 if ( count < 1 )
2022 throw new ArgumentOutOfRangeException ( nameof ( count ) , count , "Must greater than zero." ) ;
2123 if ( count > source . Count )
2224 throw new ArgumentOutOfRangeException ( nameof ( count ) , count , "Must be less than or equal to the length of the source set." ) ;
23- if ( buffer is null )
24- throw new ArgumentNullException ( nameof ( buffer ) ) ;
2525 if ( buffer . Length < count )
2626 throw new ArgumentOutOfRangeException ( nameof ( buffer ) , buffer , "Length must be greater than or equal to the provided count." ) ;
2727 Contract . EndContractBlock ( ) ;
2828
2929 return SubsetsCore ( source , count , buffer ) ;
3030
31- static IEnumerable < T [ ] > SubsetsCore ( IReadOnlyList < T > source , int count , T [ ] buffer )
31+ static IEnumerable < Memory < T > > SubsetsCore ( IReadOnlyList < T > source , int count , Memory < T > buffer )
3232 {
3333 if ( count == 1 )
3434 {
3535 foreach ( T ? e in source )
3636 {
37- buffer [ 0 ] = e ;
37+ buffer . Span [ 0 ] = e ;
3838 yield return buffer ;
3939 }
4040 yield break ;
4141 }
4242
43+ // Using an ArrayPool in this manner instead of a MemoryPool does use a few more bytes but is also slightly faster.
44+ // The result is faster enough to justify using this method.
4345 int diff = source . Count - count ;
4446 ArrayPool < int > ? pool = count > 128 ? ArrayPool < int > . Shared : null ;
4547 int [ ] ? indices = pool ? . Rent ( count ) ?? new int [ count ] ;
@@ -49,10 +51,11 @@ static IEnumerable<T[]> SubsetsCore(IReadOnlyList<T> source, int count, T[] buff
4951 int index = 0 ;
5052
5153loop :
54+ var span = buffer . Span ;
5255 while ( pos < count )
5356 {
5457 indices [ pos ] = index ;
55- buffer [ pos ] = source [ index ] ;
58+ span [ pos ] = source [ index ] ;
5659 ++ pos ;
5760 ++ index ;
5861 }
@@ -80,18 +83,11 @@ static IEnumerable<T[]> SubsetsCore(IReadOnlyList<T> source, int count, T[] buff
8083 /// <returns>An enumerable containing the resultant subsets as a memory buffer.</returns>
8184 public static IEnumerable < ReadOnlyMemory < T > > SubsetsBuffered < T > ( this IReadOnlyList < T > source , int count )
8285 {
83- ArrayPool < T > ? pool = count > 128 ? ArrayPool < T > . Shared : null ;
84- T [ ] ? buffer = pool ? . Rent ( count ) ?? new T [ count ] ;
85- var readBuffer = new ReadOnlyMemory < T > ( buffer , 0 , count ) ;
86- try
87- {
88- foreach ( T [ ] ? _ in Subsets ( source , count , buffer ) )
89- yield return readBuffer ;
90- }
91- finally
92- {
93- pool ? . Return ( buffer , true ) ;
94- }
86+ using var lease = MemoryPool < T > . Shared . Rent ( count ) ;
87+ Memory < T > buffer = lease . Memory ;
88+ ReadOnlyMemory < T > readBuffer = buffer ;
89+ foreach ( Memory < T > _ in Subsets ( source , count , buffer ) )
90+ yield return readBuffer ;
9591 }
9692
9793 /// <summary>
@@ -102,20 +98,13 @@ public static IEnumerable<ReadOnlyMemory<T>> SubsetsBuffered<T>(this IReadOnlyLi
10298 /// <returns>An enumerable containing the resultant subsets.</returns>
10399 public static IEnumerable < T [ ] > Subsets < T > ( this IReadOnlyList < T > source , int count )
104100 {
105- ArrayPool < T > ? pool = count > 128 ? ArrayPool < T > . Shared : null ;
106- T [ ] ? buffer = pool ? . Rent ( count ) ?? new T [ count ] ;
107- try
101+ using var lease = MemoryPool < T > . Shared . Rent ( count ) ;
102+ Memory < T > buffer = lease . Memory ;
103+ foreach ( Memory < T > _ in Subsets ( source , count , buffer ) )
108104 {
109- foreach ( T [ ] ? _ in Subsets ( source , count , buffer ) )
110- {
111- var a = new T [ count ] ;
112- buffer . CopyTo ( a . AsSpan ( ) ) ;
113- yield return a ;
114- }
115- }
116- finally
117- {
118- pool ? . Return ( buffer , true ) ;
105+ var a = new T [ count ] ;
106+ buffer . CopyTo ( a ) ;
107+ yield return a ;
119108 }
120109 }
121110
@@ -133,4 +122,103 @@ public static IEnumerable<ArrayPoolSegment<T>> Subsets<T>(this IReadOnlyList<T>
133122 yield return a ;
134123 }
135124 }
125+
126+ /// <inheritdoc cref="Subsets{T}(IReadOnlyList{T}, int, Memory{T})" />
127+ public static IEnumerable < Memory < T > > Subsets < T > ( this ReadOnlyMemory < T > source , int count , Memory < T > buffer )
128+ {
129+ if ( count < 1 )
130+ throw new ArgumentOutOfRangeException ( nameof ( count ) , count , "Must greater than zero." ) ;
131+ if ( count > source . Length )
132+ throw new ArgumentOutOfRangeException ( nameof ( count ) , count , "Must be less than or equal to the length of the source set." ) ;
133+ if ( buffer . Length < count )
134+ throw new ArgumentOutOfRangeException ( nameof ( buffer ) , buffer , "Length must be greater than or equal to the provided count." ) ;
135+ Contract . EndContractBlock ( ) ;
136+
137+ return SubsetsCore ( source , count , buffer ) ;
138+
139+ static IEnumerable < Memory < T > > SubsetsCore ( ReadOnlyMemory < T > source , int count , Memory < T > buffer )
140+ {
141+ if ( count == 1 )
142+ {
143+ int len = source . Length ;
144+ for ( int i = 0 ; i < len ; ++ i )
145+ {
146+ buffer . Span [ 0 ] = source . Span [ i ] ;
147+ yield return buffer ;
148+ }
149+ yield break ;
150+ }
151+
152+ // Using an ArrayPool in this manner instead of a MemoryPool does use a few more bytes but is also slightly faster.
153+ // The result is faster enough to justify using this method.
154+ int diff = source . Length - count ;
155+ ArrayPool < int > ? pool = count > 128 ? ArrayPool < int > . Shared : null ;
156+ int [ ] ? indices = pool ? . Rent ( count ) ?? new int [ count ] ;
157+ try
158+ {
159+ int pos = 0 ;
160+ int index = 0 ;
161+
162+ loop :
163+ var span = buffer . Span ;
164+ var sourceSpan = source . Span ;
165+ while ( pos < count )
166+ {
167+ indices [ pos ] = index ;
168+ span [ pos ] = sourceSpan [ index ] ;
169+ ++ pos ;
170+ ++ index ;
171+ }
172+
173+ yield return buffer ;
174+
175+ do
176+ {
177+ if ( pos == 0 ) yield break ;
178+ index = indices [ -- pos ] + 1 ;
179+ }
180+ while ( index > diff + pos ) ;
181+
182+ goto loop ;
183+ }
184+ finally
185+ {
186+ pool ? . Return ( indices ) ;
187+ }
188+ }
189+ }
190+
191+ /// <inheritdoc cref="Subsets{T}(IReadOnlyList{T}, int)"/>
192+ public static IEnumerable < ReadOnlyMemory < T > > SubsetsBuffered < T > ( this ReadOnlyMemory < T > source , int count )
193+ {
194+ using var lease = MemoryPool < T > . Shared . Rent ( count ) ;
195+ Memory < T > buffer = lease . Memory ;
196+ ReadOnlyMemory < T > readBuffer = buffer ;
197+ foreach ( Memory < T > _ in Subsets ( source , count , buffer ) )
198+ yield return readBuffer ;
199+ }
200+
201+ /// <inheritdoc cref="Subsets{T}(IReadOnlyList{T}, int)"/>
202+ public static IEnumerable < T [ ] > Subsets < T > ( this ReadOnlyMemory < T > source , int count )
203+ {
204+ using var lease = MemoryPool < T > . Shared . Rent ( count ) ;
205+ Memory < T > buffer = lease . Memory ;
206+ foreach ( Memory < T > _ in Subsets ( source , count , buffer ) )
207+ {
208+ var a = new T [ count ] ;
209+ buffer . CopyTo ( a ) ;
210+ yield return a ;
211+ }
212+ }
213+
214+ /// <inheritdoc cref="Subsets{T}(IReadOnlyList{T}, int, ArrayPool{T}?, bool)"/>
215+ public static IEnumerable < ArrayPoolSegment < T > > Subsets < T > ( this ReadOnlyMemory < T > source , int count , ArrayPool < T > ? pool , bool clearArray = false )
216+ {
217+ foreach ( ReadOnlyMemory < T > subset in SubsetsBuffered ( source , count ) )
218+ {
219+ var a = new ArrayPoolSegment < T > ( count , pool , clearArray ) ;
220+ subset . CopyTo ( a ) ;
221+ yield return a ;
222+ }
223+ }
136224}
0 commit comments