1
0

Lookup.cs 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680
  1. // Licensed to the .NET Foundation under one or more agreements.
  2. // The .NET Foundation licenses this file to you under the Apache 2.0 License.
  3. // See the LICENSE file in the project root for more information.
  4. using System.Collections;
  5. using System.Collections.Generic;
  6. using System.Diagnostics;
  7. using System.Threading;
  8. using System.Threading.Tasks;
  9. // This is internal because System.Linq exposes a public Lookup that we cannot directly use here
  10. namespace System.Linq.Internal
  11. {
  12. internal class Lookup<TKey, TElement> : ILookup<TKey, TElement>, IAsyncIListProvider<IAsyncGrouping<TKey, TElement>>
  13. {
  14. private readonly IEqualityComparer<TKey> _comparer;
  15. private Grouping<TKey, TElement>[] _groupings;
  16. private Grouping<TKey, TElement>? _lastGrouping;
  17. private Lookup(IEqualityComparer<TKey>? comparer)
  18. {
  19. _comparer = comparer ?? EqualityComparer<TKey>.Default;
  20. _groupings = new Grouping<TKey, TElement>[7];
  21. }
  22. public int Count { get; private set; }
  23. public IEnumerable<TElement> this[TKey key]
  24. {
  25. get
  26. {
  27. var grouping = GetGrouping(key);
  28. if (grouping != null)
  29. {
  30. return grouping;
  31. }
  32. #if NO_ARRAY_EMPTY
  33. return EmptyArray<TElement>.Value;
  34. #else
  35. return Array.Empty<TElement>();
  36. #endif
  37. }
  38. }
  39. public bool Contains(TKey key)
  40. {
  41. return GetGrouping(key) != null;
  42. }
  43. IEnumerator IEnumerable.GetEnumerator()
  44. {
  45. return GetEnumerator();
  46. }
  47. public IEnumerator<IGrouping<TKey, TElement>> GetEnumerator()
  48. {
  49. var g = _lastGrouping;
  50. if (g != null)
  51. {
  52. do
  53. {
  54. g = g!._next;
  55. yield return g!;
  56. } while (g != _lastGrouping);
  57. }
  58. }
  59. public IEnumerable<TResult> ApplyResultSelector<TResult>(Func<TKey, IAsyncEnumerable<TElement>, TResult> resultSelector)
  60. {
  61. var g = _lastGrouping;
  62. if (g != null)
  63. {
  64. do
  65. {
  66. g = g._next;
  67. g!.Trim();
  68. var result = resultSelector(g._key, g._elements.ToAsyncEnumerable());
  69. yield return result;
  70. } while (g != _lastGrouping);
  71. }
  72. }
  73. internal static async Task<Lookup<TKey, TElement>> CreateAsync<TSource>(IAsyncEnumerable<TSource> source, Func<TSource, TKey> keySelector, Func<TSource, TElement> elementSelector, IEqualityComparer<TKey>? comparer, CancellationToken cancellationToken)
  74. {
  75. Debug.Assert(source != null);
  76. Debug.Assert(keySelector != null);
  77. Debug.Assert(elementSelector != null);
  78. var lookup = new Lookup<TKey, TElement>(comparer);
  79. await foreach (var item in source.WithCancellation(cancellationToken).ConfigureAwait(false))
  80. {
  81. var key = keySelector(item);
  82. var group = lookup.GetOrCreateGrouping(key);
  83. var element = elementSelector(item);
  84. group.Add(element);
  85. }
  86. return lookup;
  87. }
  88. internal static async Task<Lookup<TKey, TElement>> CreateAsync(IAsyncEnumerable<TElement> source, Func<TElement, TKey> keySelector, IEqualityComparer<TKey>? comparer, CancellationToken cancellationToken)
  89. {
  90. Debug.Assert(source != null);
  91. Debug.Assert(keySelector != null);
  92. var lookup = new Lookup<TKey, TElement>(comparer);
  93. await foreach (var item in source.WithCancellation(cancellationToken).ConfigureAwait(false))
  94. {
  95. var key = keySelector(item);
  96. lookup.GetOrCreateGrouping(key).Add(item);
  97. }
  98. return lookup;
  99. }
  100. internal static async Task<Lookup<TKey, TElement>> CreateForJoinAsync(IAsyncEnumerable<TElement> source, Func<TElement, TKey> keySelector, IEqualityComparer<TKey>? comparer, CancellationToken cancellationToken)
  101. {
  102. var lookup = new Lookup<TKey, TElement>(comparer);
  103. await foreach (var item in source.WithCancellation(cancellationToken).ConfigureAwait(false))
  104. {
  105. var key = keySelector(item);
  106. if (key != null)
  107. {
  108. lookup.GetOrCreateGrouping(key).Add(item);
  109. }
  110. }
  111. return lookup;
  112. }
  113. internal Grouping<TKey, TElement>? GetGrouping(TKey key)
  114. {
  115. var hashCode = InternalGetHashCode(key);
  116. return GetGrouping(key, hashCode);
  117. }
  118. internal Grouping<TKey, TElement>? GetGrouping(TKey key, int hashCode)
  119. {
  120. for (var g = _groupings[hashCode % _groupings.Length]; g != null; g = g._hashNext)
  121. {
  122. if (g._hashCode == hashCode && _comparer.Equals(g._key, key))
  123. {
  124. return g;
  125. }
  126. }
  127. return null;
  128. }
  129. internal Grouping<TKey, TElement> GetOrCreateGrouping(TKey key)
  130. {
  131. var hashCode = InternalGetHashCode(key);
  132. var grouping = GetGrouping(key, hashCode);
  133. if (grouping != null)
  134. {
  135. return grouping;
  136. }
  137. if (Count == _groupings.Length)
  138. {
  139. Resize();
  140. }
  141. var index = hashCode % _groupings.Length;
  142. var g = new Grouping<TKey, TElement>(key, hashCode, new TElement[1], _groupings[index]);
  143. _groupings[index] = g;
  144. if (_lastGrouping == null)
  145. {
  146. g._next = g;
  147. }
  148. else
  149. {
  150. g._next = _lastGrouping._next;
  151. _lastGrouping._next = g;
  152. }
  153. _lastGrouping = g;
  154. Count++;
  155. return g;
  156. }
  157. internal int InternalGetHashCode(TKey key)
  158. {
  159. // Handle comparer implementations that throw when passed null
  160. return (key == null) ? 0 : _comparer.GetHashCode(key) & 0x7FFFFFFF;
  161. }
  162. internal TResult[] ToArray<TResult>(Func<TKey, IAsyncEnumerable<TElement>, TResult> resultSelector)
  163. {
  164. var array = new TResult[Count];
  165. var index = 0;
  166. var g = _lastGrouping;
  167. if (g != null)
  168. {
  169. do
  170. {
  171. g = g._next;
  172. g!.Trim();
  173. array[index] = resultSelector(g._key, g._elements.ToAsyncEnumerable());
  174. ++index;
  175. } while (g != _lastGrouping);
  176. }
  177. return array;
  178. }
  179. internal List<TResult> ToList<TResult>(Func<TKey, IAsyncEnumerable<TElement>, TResult> resultSelector)
  180. {
  181. var list = new List<TResult>(Count);
  182. var g = _lastGrouping;
  183. if (g != null)
  184. {
  185. do
  186. {
  187. g = g._next;
  188. g!.Trim();
  189. var result = resultSelector(g._key, g._elements.ToAsyncEnumerable());
  190. list.Add(result);
  191. } while (g != _lastGrouping);
  192. }
  193. return list;
  194. }
  195. private void Resize()
  196. {
  197. var newSize = checked((Count * 2) + 1);
  198. var newGroupings = new Grouping<TKey, TElement>[newSize];
  199. var g = _lastGrouping;
  200. do
  201. {
  202. g = g!._next;
  203. var index = g!._hashCode % newSize;
  204. g._hashNext = newGroupings[index];
  205. newGroupings[index] = g;
  206. } while (g != _lastGrouping);
  207. _groupings = newGroupings;
  208. }
  209. public ValueTask<int> GetCountAsync(bool onlyIfCheap, CancellationToken cancellationToken)
  210. {
  211. return new ValueTask<int>(Count);
  212. }
  213. IAsyncEnumerator<IAsyncGrouping<TKey, TElement>> IAsyncEnumerable<IAsyncGrouping<TKey, TElement>>.GetAsyncEnumerator(CancellationToken cancellationToken)
  214. {
  215. cancellationToken.ThrowIfCancellationRequested(); // NB: [LDM-2018-11-28] Equivalent to async iterator behavior.
  216. return Enumerable.Cast<IAsyncGrouping<TKey, TElement>>(this).ToAsyncEnumerable().GetAsyncEnumerator(cancellationToken);
  217. }
  218. ValueTask<List<IAsyncGrouping<TKey, TElement>>> IAsyncIListProvider<IAsyncGrouping<TKey, TElement>>.ToListAsync(CancellationToken cancellationToken)
  219. {
  220. cancellationToken.ThrowIfCancellationRequested();
  221. var list = new List<IAsyncGrouping<TKey, TElement>>(Count);
  222. var g = _lastGrouping;
  223. if (g != null)
  224. {
  225. do
  226. {
  227. g = g!._next;
  228. list.Add(g!);
  229. }
  230. while (g != _lastGrouping);
  231. }
  232. return new ValueTask<List<IAsyncGrouping<TKey, TElement>>>(list);
  233. }
  234. ValueTask<IAsyncGrouping<TKey, TElement>[]> IAsyncIListProvider<IAsyncGrouping<TKey, TElement>>.ToArrayAsync(CancellationToken cancellationToken)
  235. {
  236. cancellationToken.ThrowIfCancellationRequested();
  237. var array = new IAsyncGrouping<TKey, TElement>[Count];
  238. var index = 0;
  239. var g = _lastGrouping;
  240. if (g != null)
  241. {
  242. do
  243. {
  244. g = g!._next;
  245. array[index] = g!;
  246. ++index;
  247. }
  248. while (g != _lastGrouping);
  249. }
  250. return new ValueTask<IAsyncGrouping<TKey, TElement>[]>(array);
  251. }
  252. }
  253. internal class LookupWithTask<TKey, TElement> : ILookup<TKey, TElement>, IAsyncIListProvider<IAsyncGrouping<TKey, TElement>>
  254. {
  255. private readonly IEqualityComparer<TKey> _comparer;
  256. private Grouping<TKey, TElement>[] _groupings;
  257. private Grouping<TKey, TElement>? _lastGrouping;
  258. private LookupWithTask(IEqualityComparer<TKey>? comparer)
  259. {
  260. _comparer = comparer ?? EqualityComparer<TKey>.Default;
  261. _groupings = new Grouping<TKey, TElement>[7];
  262. }
  263. public int Count { get; private set; }
  264. public IEnumerable<TElement> this[TKey key]
  265. {
  266. get
  267. {
  268. var grouping = GetGrouping(key);
  269. if (grouping != null)
  270. {
  271. return grouping;
  272. }
  273. #if NO_ARRAY_EMPTY
  274. return EmptyArray<TElement>.Value;
  275. #else
  276. return Array.Empty<TElement>();
  277. #endif
  278. }
  279. }
  280. public bool Contains(TKey key)
  281. {
  282. return GetGrouping(key) != null;
  283. }
  284. IEnumerator IEnumerable.GetEnumerator()
  285. {
  286. return GetEnumerator();
  287. }
  288. public IEnumerator<IGrouping<TKey, TElement>> GetEnumerator()
  289. {
  290. var g = _lastGrouping;
  291. if (g != null)
  292. {
  293. do
  294. {
  295. g = g!._next;
  296. yield return g!;
  297. } while (g != _lastGrouping);
  298. }
  299. }
  300. internal static async Task<LookupWithTask<TKey, TElement>> CreateAsync<TSource>(IAsyncEnumerable<TSource> source, Func<TSource, ValueTask<TKey>> keySelector, Func<TSource, ValueTask<TElement>> elementSelector, IEqualityComparer<TKey>? comparer, CancellationToken cancellationToken)
  301. {
  302. Debug.Assert(source != null);
  303. Debug.Assert(keySelector != null);
  304. Debug.Assert(elementSelector != null);
  305. var lookup = new LookupWithTask<TKey, TElement>(comparer);
  306. await foreach (var item in source.WithCancellation(cancellationToken).ConfigureAwait(false))
  307. {
  308. var key = await keySelector(item).ConfigureAwait(false);
  309. var group = lookup.GetOrCreateGrouping(key);
  310. var element = await elementSelector(item).ConfigureAwait(false);
  311. group.Add(element);
  312. }
  313. return lookup;
  314. }
  315. #if !NO_DEEP_CANCELLATION
  316. internal static async Task<LookupWithTask<TKey, TElement>> CreateAsync<TSource>(IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<TKey>> keySelector, Func<TSource, CancellationToken, ValueTask<TElement>> elementSelector, IEqualityComparer<TKey>? comparer, CancellationToken cancellationToken)
  317. {
  318. Debug.Assert(source != null);
  319. Debug.Assert(keySelector != null);
  320. Debug.Assert(elementSelector != null);
  321. var lookup = new LookupWithTask<TKey, TElement>(comparer);
  322. await foreach (var item in source.WithCancellation(cancellationToken).ConfigureAwait(false))
  323. {
  324. var key = await keySelector(item, cancellationToken).ConfigureAwait(false);
  325. var group = lookup.GetOrCreateGrouping(key);
  326. var element = await elementSelector(item, cancellationToken).ConfigureAwait(false);
  327. group.Add(element);
  328. }
  329. return lookup;
  330. }
  331. #endif
  332. internal static async Task<LookupWithTask<TKey, TElement>> CreateAsync(IAsyncEnumerable<TElement> source, Func<TElement, ValueTask<TKey>> keySelector, IEqualityComparer<TKey>? comparer, CancellationToken cancellationToken)
  333. {
  334. Debug.Assert(source != null);
  335. Debug.Assert(keySelector != null);
  336. var lookup = new LookupWithTask<TKey, TElement>(comparer);
  337. await foreach (var item in source.WithCancellation(cancellationToken).ConfigureAwait(false))
  338. {
  339. var key = await keySelector(item).ConfigureAwait(false);
  340. lookup.GetOrCreateGrouping(key).Add(item);
  341. }
  342. return lookup;
  343. }
  344. #if !NO_DEEP_CANCELLATION
  345. internal static async Task<LookupWithTask<TKey, TElement>> CreateAsync(IAsyncEnumerable<TElement> source, Func<TElement, CancellationToken, ValueTask<TKey>> keySelector, IEqualityComparer<TKey>? comparer, CancellationToken cancellationToken)
  346. {
  347. Debug.Assert(source != null);
  348. Debug.Assert(keySelector != null);
  349. var lookup = new LookupWithTask<TKey, TElement>(comparer);
  350. await foreach (var item in source.WithCancellation(cancellationToken).ConfigureAwait(false))
  351. {
  352. var key = await keySelector(item, cancellationToken).ConfigureAwait(false);
  353. lookup.GetOrCreateGrouping(key).Add(item);
  354. }
  355. return lookup;
  356. }
  357. #endif
  358. internal static async Task<LookupWithTask<TKey, TElement>> CreateForJoinAsync(IAsyncEnumerable<TElement> source, Func<TElement, ValueTask<TKey>> keySelector, IEqualityComparer<TKey>? comparer, CancellationToken cancellationToken)
  359. {
  360. var lookup = new LookupWithTask<TKey, TElement>(comparer);
  361. await foreach (var item in source.WithCancellation(cancellationToken).ConfigureAwait(false))
  362. {
  363. var key = await keySelector(item).ConfigureAwait(false);
  364. if (key != null)
  365. {
  366. lookup.GetOrCreateGrouping(key).Add(item);
  367. }
  368. }
  369. return lookup;
  370. }
  371. #if !NO_DEEP_CANCELLATION
  372. internal static async Task<LookupWithTask<TKey, TElement>> CreateForJoinAsync(IAsyncEnumerable<TElement> source, Func<TElement, CancellationToken, ValueTask<TKey>> keySelector, IEqualityComparer<TKey>? comparer, CancellationToken cancellationToken)
  373. {
  374. var lookup = new LookupWithTask<TKey, TElement>(comparer);
  375. await foreach (var item in source.WithCancellation(cancellationToken).ConfigureAwait(false))
  376. {
  377. var key = await keySelector(item, cancellationToken).ConfigureAwait(false);
  378. if (key != null)
  379. {
  380. lookup.GetOrCreateGrouping(key).Add(item);
  381. }
  382. }
  383. return lookup;
  384. }
  385. #endif
  386. internal Grouping<TKey, TElement>? GetGrouping(TKey key)
  387. {
  388. var hashCode = InternalGetHashCode(key);
  389. return GetGrouping(key, hashCode);
  390. }
  391. internal Grouping<TKey, TElement>? GetGrouping(TKey key, int hashCode)
  392. {
  393. for (var g = _groupings[hashCode % _groupings.Length]; g != null; g = g._hashNext)
  394. {
  395. if (g._hashCode == hashCode && _comparer.Equals(g._key, key))
  396. {
  397. return g;
  398. }
  399. }
  400. return null;
  401. }
  402. internal Grouping<TKey, TElement> GetOrCreateGrouping(TKey key)
  403. {
  404. var hashCode = InternalGetHashCode(key);
  405. var grouping = GetGrouping(key, hashCode);
  406. if (grouping != null)
  407. {
  408. return grouping;
  409. }
  410. if (Count == _groupings.Length)
  411. {
  412. Resize();
  413. }
  414. var index = hashCode % _groupings.Length;
  415. var g = new Grouping<TKey, TElement>(key, hashCode, new TElement[1], _groupings[index]);
  416. _groupings[index] = g;
  417. if (_lastGrouping == null)
  418. {
  419. g._next = g;
  420. }
  421. else
  422. {
  423. g._next = _lastGrouping._next;
  424. _lastGrouping._next = g;
  425. }
  426. _lastGrouping = g;
  427. Count++;
  428. return g;
  429. }
  430. internal int InternalGetHashCode(TKey key)
  431. {
  432. // Handle comparer implementations that throw when passed null
  433. return (key == null) ? 0 : _comparer.GetHashCode(key) & 0x7FFFFFFF;
  434. }
  435. internal async Task<TResult[]> ToArray<TResult>(Func<TKey, IAsyncEnumerable<TElement>, ValueTask<TResult>> resultSelector)
  436. {
  437. var array = new TResult[Count];
  438. var index = 0;
  439. var g = _lastGrouping;
  440. if (g != null)
  441. {
  442. do
  443. {
  444. g = g._next;
  445. g!.Trim();
  446. array[index] = await resultSelector(g._key, g._elements.ToAsyncEnumerable()).ConfigureAwait(false);
  447. ++index;
  448. } while (g != _lastGrouping);
  449. }
  450. return array;
  451. }
  452. #if !NO_DEEP_CANCELLATION
  453. internal async Task<TResult[]> ToArray<TResult>(Func<TKey, IAsyncEnumerable<TElement>, CancellationToken, ValueTask<TResult>> resultSelector, CancellationToken cancellationToken)
  454. {
  455. cancellationToken.ThrowIfCancellationRequested();
  456. var array = new TResult[Count];
  457. var index = 0;
  458. var g = _lastGrouping;
  459. if (g != null)
  460. {
  461. do
  462. {
  463. g = g._next;
  464. g!.Trim();
  465. array[index] = await resultSelector(g._key, g._elements.ToAsyncEnumerable(), cancellationToken).ConfigureAwait(false);
  466. ++index;
  467. } while (g != _lastGrouping);
  468. }
  469. return array;
  470. }
  471. #endif
  472. internal async Task<List<TResult>> ToList<TResult>(Func<TKey, IAsyncEnumerable<TElement>, ValueTask<TResult>> resultSelector)
  473. {
  474. var list = new List<TResult>(Count);
  475. var g = _lastGrouping;
  476. if (g != null)
  477. {
  478. do
  479. {
  480. g = g._next;
  481. g!.Trim();
  482. var result = await resultSelector(g._key, g._elements.ToAsyncEnumerable()).ConfigureAwait(false);
  483. list.Add(result);
  484. } while (g != _lastGrouping);
  485. }
  486. return list;
  487. }
  488. #if !NO_DEEP_CANCELLATION
  489. internal async Task<List<TResult>> ToList<TResult>(Func<TKey, IAsyncEnumerable<TElement>, CancellationToken, ValueTask<TResult>> resultSelector, CancellationToken cancellationToken)
  490. {
  491. cancellationToken.ThrowIfCancellationRequested();
  492. var list = new List<TResult>(Count);
  493. var g = _lastGrouping;
  494. if (g != null)
  495. {
  496. do
  497. {
  498. g = g._next;
  499. g!.Trim();
  500. var result = await resultSelector(g._key, g._elements.ToAsyncEnumerable(), cancellationToken).ConfigureAwait(false);
  501. list.Add(result);
  502. } while (g != _lastGrouping);
  503. }
  504. return list;
  505. }
  506. #endif
  507. private void Resize()
  508. {
  509. var newSize = checked((Count * 2) + 1);
  510. var newGroupings = new Grouping<TKey, TElement>[newSize];
  511. var g = _lastGrouping;
  512. do
  513. {
  514. g = g!._next;
  515. var index = g!._hashCode % newSize;
  516. g._hashNext = newGroupings[index];
  517. newGroupings[index] = g;
  518. } while (g != _lastGrouping);
  519. _groupings = newGroupings;
  520. }
  521. public ValueTask<int> GetCountAsync(bool onlyIfCheap, CancellationToken cancellationToken)
  522. {
  523. return new ValueTask<int>(Count);
  524. }
  525. IAsyncEnumerator<IAsyncGrouping<TKey, TElement>> IAsyncEnumerable<IAsyncGrouping<TKey, TElement>>.GetAsyncEnumerator(CancellationToken cancellationToken)
  526. {
  527. cancellationToken.ThrowIfCancellationRequested(); // NB: [LDM-2018-11-28] Equivalent to async iterator behavior.
  528. return Enumerable.Cast<IAsyncGrouping<TKey, TElement>>(this).ToAsyncEnumerable().GetAsyncEnumerator(cancellationToken);
  529. }
  530. ValueTask<List<IAsyncGrouping<TKey, TElement>>> IAsyncIListProvider<IAsyncGrouping<TKey, TElement>>.ToListAsync(CancellationToken cancellationToken)
  531. {
  532. cancellationToken.ThrowIfCancellationRequested();
  533. var list = new List<IAsyncGrouping<TKey, TElement>>(Count);
  534. var g = _lastGrouping;
  535. if (g != null)
  536. {
  537. do
  538. {
  539. g = g!._next;
  540. list.Add(g!);
  541. }
  542. while (g != _lastGrouping);
  543. }
  544. return new ValueTask<List<IAsyncGrouping<TKey, TElement>>>(list);
  545. }
  546. ValueTask<IAsyncGrouping<TKey, TElement>[]> IAsyncIListProvider<IAsyncGrouping<TKey, TElement>>.ToArrayAsync(CancellationToken cancellationToken)
  547. {
  548. cancellationToken.ThrowIfCancellationRequested();
  549. var array = new IAsyncGrouping<TKey, TElement>[Count];
  550. var index = 0;
  551. var g = _lastGrouping;
  552. if (g != null)
  553. {
  554. do
  555. {
  556. g = g!._next;
  557. array[index] = g!;
  558. ++index;
  559. }
  560. while (g != _lastGrouping);
  561. }
  562. return new ValueTask<IAsyncGrouping<TKey, TElement>[]>(array);
  563. }
  564. }
  565. }