Lookup.cs 22 KB

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