@@ -42,7 +42,7 @@ public static IStreamingSource<TResult> MergeJoin<TLeft, TRight, TKey, TResult>(
4242 ArgumentNullException . ThrowIfNull ( right ) ;
4343 ArgumentNullException . ThrowIfNull ( keyRight ) ;
4444 ArgumentNullException . ThrowIfNull ( map ) ;
45- var comparer = keyComparer ?? Comparer < TKey > . Default ;
45+ IComparer < TKey > comparer = keyComparer ?? Comparer < TKey > . Default ;
4646
4747 async IAsyncEnumerable < TResult > Merge (
4848 ReportExecutionContext execution , [ EnumeratorCancellation ] CancellationToken cancellationToken )
@@ -52,7 +52,7 @@ async IAsyncEnumerable<TResult> Merge(
5252 var rightHasCurrent = await rightEnum . MoveNextAsync ( ) . ConfigureAwait ( false ) ;
5353
5454 var group = new List < TRight > ( ) ;
55- IReadOnlyList < TRight > current = Array . Empty < TRight > ( ) ;
55+ TRight [ ] current = Array . Empty < TRight > ( ) ;
5656 TKey ? currentKey = default ;
5757 var haveKey = false ;
5858
@@ -61,28 +61,44 @@ async IAsyncEnumerable<TResult> Merge(
6161 TKey key = keyLeft ( leftRow ) ;
6262 if ( ! haveKey || comparer . Compare ( currentKey ! , key ) != 0 )
6363 {
64- group . Clear ( ) ;
65- while ( rightHasCurrent && comparer . Compare ( keyRight ( rightEnum . Current ) , key ) < 0 )
66- rightHasCurrent = await rightEnum . MoveNextAsync ( ) . ConfigureAwait ( false ) ;
67- while ( rightHasCurrent && comparer . Compare ( keyRight ( rightEnum . Current ) , key ) == 0 )
68- {
69- group . Add ( rightEnum . Current ) ;
70- rightHasCurrent = await rightEnum . MoveNextAsync ( ) . ConfigureAwait ( false ) ;
71- }
72-
64+ rightHasCurrent = await GatherGroupAsync ( rightEnum , rightHasCurrent , key , keyRight , comparer , group )
65+ . ConfigureAwait ( false ) ;
7366 current = group . Count == 0 ? Array . Empty < TRight > ( ) : group . ToArray ( ) ;
7467 currentKey = key ;
7568 haveKey = true ;
7669 }
7770
78- if ( current . Count > 0 || kind == JoinKind . LeftOuter )
71+ if ( current . Length > 0 || kind == JoinKind . LeftOuter )
7972 yield return map ( leftRow , current ) ;
8073 }
8174 }
8275
8376 return new DelegatingStreamingSource < TResult > ( left . Schema , Merge ) ;
8477 }
8578
79+ // Advances the right enumerator past keys below <paramref name="key"/>, then buffers the
80+ // contiguous group of right rows whose key equals it. Returns whether the right enumerator still
81+ // has a current row.
82+ private static async Task < bool > GatherGroupAsync < TRight , TKey > (
83+ IAsyncEnumerator < TRight > right ,
84+ bool hasCurrent ,
85+ TKey key ,
86+ Func < TRight , TKey > keyRight ,
87+ IComparer < TKey > comparer ,
88+ List < TRight > group )
89+ {
90+ group . Clear ( ) ;
91+ while ( hasCurrent && comparer . Compare ( keyRight ( right . Current ) , key ) < 0 )
92+ hasCurrent = await right . MoveNextAsync ( ) . ConfigureAwait ( false ) ;
93+ while ( hasCurrent && comparer . Compare ( keyRight ( right . Current ) , key ) == 0 )
94+ {
95+ group . Add ( right . Current ) ;
96+ hasCurrent = await right . MoveNextAsync ( ) . ConfigureAwait ( false ) ;
97+ }
98+
99+ return hasCurrent ;
100+ }
101+
86102 /// <summary>Reads a batch source page by page as a flat async sequence (O(pageSize) memory).</summary>
87103 private static async IAsyncEnumerable < T > Paginate < T > (
88104 IBatchSource < T > source , ReportExecutionContext execution , [ EnumeratorCancellation ] CancellationToken cancellationToken )
0 commit comments