AppendPrepend.cs 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510
  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;
  5. using System.Collections.Generic;
  6. using System.Diagnostics;
  7. using System.Linq;
  8. using System.Threading;
  9. using System.Threading.Tasks;
  10. namespace System.Linq
  11. {
  12. public static partial class AsyncEnumerable
  13. {
  14. public static IAsyncEnumerable<TSource> Append<TSource>(this IAsyncEnumerable<TSource> source, TSource element)
  15. {
  16. if (source == null)
  17. throw new ArgumentNullException(nameof(source));
  18. var appendable = source as AppendPrepentAsyncIterator<TSource>;
  19. if (appendable != null)
  20. {
  21. return appendable.Append(element);
  22. }
  23. return new AppendPrepend1AsyncIterator<TSource>(source, element, true);
  24. }
  25. public static IAsyncEnumerable<TSource> Prepend<TSource>(this IAsyncEnumerable<TSource> source, TSource element)
  26. {
  27. if (source == null)
  28. throw new ArgumentNullException(nameof(source));
  29. var appendable = source as AppendPrepentAsyncIterator<TSource>;
  30. if (appendable != null)
  31. {
  32. return appendable.Prepend(element);
  33. }
  34. return new AppendPrepend1AsyncIterator<TSource>(source, element, false);
  35. }
  36. private abstract class AppendPrepentAsyncIterator<TSource> : AsyncIterator<TSource>, IIListProvider<TSource>
  37. {
  38. protected readonly IAsyncEnumerable<TSource> source;
  39. protected IAsyncEnumerator<TSource> enumerator;
  40. protected AppendPrepentAsyncIterator(IAsyncEnumerable<TSource> source)
  41. {
  42. Debug.Assert(source != null);
  43. this.source = source;
  44. }
  45. protected void GetSourceEnumerator()
  46. {
  47. Debug.Assert(enumerator == null);
  48. enumerator = source.GetEnumerator();
  49. }
  50. public abstract AppendPrepentAsyncIterator<TSource> Append(TSource item);
  51. public abstract AppendPrepentAsyncIterator<TSource> Prepend(TSource item);
  52. protected async Task<bool> LoadFromEnumerator(CancellationToken cancellationToken)
  53. {
  54. if (await enumerator.MoveNext(cancellationToken)
  55. .ConfigureAwait(false))
  56. {
  57. current = enumerator.Current;
  58. return true;
  59. }
  60. enumerator?.Dispose();
  61. enumerator = null;
  62. return false;
  63. }
  64. public override void Dispose()
  65. {
  66. if (enumerator != null)
  67. {
  68. enumerator.Dispose();
  69. enumerator = null;
  70. }
  71. base.Dispose();
  72. }
  73. public abstract Task<TSource[]> ToArrayAsync(CancellationToken cancellationToken);
  74. public abstract Task<List<TSource>> ToListAsync(CancellationToken cancellationToken);
  75. public abstract Task<int> GetCountAsync(bool onlyIfCheap, CancellationToken cancellationToken);
  76. }
  77. private sealed class AppendPrepend1AsyncIterator<TSource> : AppendPrepentAsyncIterator<TSource>
  78. {
  79. private readonly TSource item;
  80. private readonly bool appending;
  81. bool hasEnumerator;
  82. public AppendPrepend1AsyncIterator(IAsyncEnumerable<TSource> source, TSource item, bool appending)
  83. : base(source)
  84. {
  85. this.item = item;
  86. this.appending = appending;
  87. }
  88. public override AsyncIterator<TSource> Clone()
  89. {
  90. return new AppendPrepend1AsyncIterator<TSource>(source, item, appending);
  91. }
  92. protected override async Task<bool> MoveNextCore(CancellationToken cancellationToken)
  93. {
  94. switch (state)
  95. {
  96. case AsyncIteratorState.Allocated:
  97. hasEnumerator = false;
  98. state = AsyncIteratorState.Iterating;
  99. if (!appending)
  100. {
  101. current = item;
  102. return true;
  103. }
  104. goto case AsyncIteratorState.Iterating;
  105. case AsyncIteratorState.Iterating:
  106. if (!hasEnumerator)
  107. {
  108. GetSourceEnumerator();
  109. hasEnumerator = true;
  110. }
  111. if (enumerator != null)
  112. {
  113. if (await LoadFromEnumerator(cancellationToken)
  114. .ConfigureAwait(false))
  115. {
  116. return true;
  117. }
  118. if (appending)
  119. {
  120. current = item;
  121. return true;
  122. }
  123. }
  124. break;
  125. }
  126. Dispose();
  127. return false;
  128. }
  129. public override AppendPrepentAsyncIterator<TSource> Append(TSource element)
  130. {
  131. if (appending)
  132. {
  133. return new AppendPrependNAsyncIterator<TSource>(source, null, new SingleLinkedNode<TSource>(item, element));
  134. }
  135. return new AppendPrependNAsyncIterator<TSource>(source, new SingleLinkedNode<TSource>(item), new SingleLinkedNode<TSource>(element));
  136. }
  137. public override AppendPrepentAsyncIterator<TSource> Prepend(TSource element)
  138. {
  139. if (appending)
  140. {
  141. return new AppendPrependNAsyncIterator<TSource>(source, new SingleLinkedNode<TSource>(element), new SingleLinkedNode<TSource>(item));
  142. }
  143. return new AppendPrependNAsyncIterator<TSource>(source, new SingleLinkedNode<TSource>(item, element), null);
  144. }
  145. public override async Task<TSource[]> ToArrayAsync(CancellationToken cancellationToken)
  146. {
  147. var count = await GetCountAsync(onlyIfCheap: true, cancellationToken: cancellationToken).ConfigureAwait(false);
  148. if (count == -1)
  149. {
  150. return await AsyncEnumerableHelpers.ToArray(this, cancellationToken).ConfigureAwait(false);
  151. }
  152. var array = new TSource[count];
  153. int index;
  154. if (appending)
  155. {
  156. index = 0;
  157. }
  158. else
  159. {
  160. array[0] = item;
  161. index = 1;
  162. }
  163. var sourceCollection = source as ICollection<TSource>;
  164. if (sourceCollection != null)
  165. {
  166. sourceCollection.CopyTo(array, index);
  167. }
  168. else
  169. {
  170. using (var en = source.GetEnumerator())
  171. {
  172. while (await en.MoveNext(cancellationToken)
  173. .ConfigureAwait(false))
  174. {
  175. array[index] = en.Current;
  176. ++index;
  177. }
  178. }
  179. }
  180. if (appending)
  181. {
  182. array[array.Length - 1] = item;
  183. }
  184. return array;
  185. }
  186. public override async Task<List<TSource>> ToListAsync(CancellationToken cancellationToken)
  187. {
  188. var count = await GetCountAsync(onlyIfCheap: true, cancellationToken: cancellationToken).ConfigureAwait(false);
  189. var list = count == -1 ? new List<TSource>() : new List<TSource>(count);
  190. if (!appending)
  191. {
  192. list.Add(item);
  193. }
  194. using (var en = source.GetEnumerator())
  195. {
  196. while (await en.MoveNext(cancellationToken)
  197. .ConfigureAwait(false))
  198. {
  199. list.Add(en.Current);
  200. }
  201. }
  202. if (appending)
  203. {
  204. list.Add(item);
  205. }
  206. return list;
  207. }
  208. public override async Task<int> GetCountAsync(bool onlyIfCheap, CancellationToken cancellationToken)
  209. {
  210. var listProv = source as IIListProvider<TSource>;
  211. if (listProv != null)
  212. {
  213. var count = await listProv.GetCountAsync(onlyIfCheap, cancellationToken).ConfigureAwait(false);
  214. return count == -1 ? -1 : count + 1;
  215. }
  216. return !onlyIfCheap || source is ICollection<TSource> ? await source.Count(cancellationToken).ConfigureAwait(false) + 1 : -1;
  217. }
  218. }
  219. private sealed class SingleLinkedNode<TSource>
  220. {
  221. public SingleLinkedNode(TSource first, TSource second)
  222. {
  223. Linked = new SingleLinkedNode<TSource>(first);
  224. Item = second;
  225. Count = 2;
  226. }
  227. public SingleLinkedNode(TSource item)
  228. {
  229. Item = item;
  230. Count = 1;
  231. }
  232. private SingleLinkedNode(SingleLinkedNode<TSource> linked, TSource item)
  233. {
  234. Debug.Assert(linked != null);
  235. Linked = linked;
  236. Item = item;
  237. Count = linked.Count + 1;
  238. }
  239. public TSource Item { get; }
  240. public SingleLinkedNode<TSource> Linked { get; }
  241. public int Count { get; }
  242. public SingleLinkedNode<TSource> Add(TSource item) => new SingleLinkedNode<TSource>(this, item);
  243. public IEnumerator<TSource> GetEnumerator()
  244. {
  245. var array = new TSource[Count];
  246. var index = Count;
  247. for (var n = this; n != null; n = n.Linked)
  248. {
  249. --index;
  250. array[index] = n.Item;
  251. }
  252. Debug.Assert(index == 0);
  253. return ((IEnumerable<TSource>)array).GetEnumerator();
  254. }
  255. }
  256. private sealed class AppendPrependNAsyncIterator<TSource> : AppendPrepentAsyncIterator<TSource>
  257. {
  258. private readonly SingleLinkedNode<TSource> prepended;
  259. private readonly SingleLinkedNode<TSource> appended;
  260. private SingleLinkedNode<TSource> node;
  261. public AppendPrependNAsyncIterator(IAsyncEnumerable<TSource> source, SingleLinkedNode<TSource> prepended, SingleLinkedNode<TSource> appended)
  262. : base(source)
  263. {
  264. Debug.Assert(prepended != null || appended != null);
  265. this.prepended = prepended;
  266. this.appended = appended;
  267. }
  268. public override AsyncIterator<TSource> Clone()
  269. {
  270. return new AppendPrependNAsyncIterator<TSource>(source, prepended, appended);
  271. }
  272. int mode;
  273. IEnumerator<TSource> appendedEnumerator;
  274. public override void Dispose()
  275. {
  276. if (appendedEnumerator != null)
  277. {
  278. appendedEnumerator.Dispose();
  279. appendedEnumerator = null;
  280. }
  281. base.Dispose();
  282. }
  283. protected override async Task<bool> MoveNextCore(CancellationToken cancellationToken)
  284. {
  285. switch (state)
  286. {
  287. case AsyncIteratorState.Allocated:
  288. mode = 1;
  289. state = AsyncIteratorState.Iterating;
  290. goto case AsyncIteratorState.Iterating;
  291. case AsyncIteratorState.Iterating:
  292. switch (mode)
  293. {
  294. case 1:
  295. node = prepended;
  296. mode = 2;
  297. goto case 2;
  298. case 2:
  299. if (node != null)
  300. {
  301. current = node.Item;
  302. node = node.Linked;
  303. return true;
  304. }
  305. GetSourceEnumerator();
  306. mode = 3;
  307. goto case 3;
  308. case 3:
  309. if (await LoadFromEnumerator(cancellationToken)
  310. .ConfigureAwait(false))
  311. {
  312. return true;
  313. }
  314. if (appended != null)
  315. {
  316. appendedEnumerator = appended.GetEnumerator();
  317. mode = 4;
  318. goto case 4;
  319. }
  320. break;
  321. case 4:
  322. if (appendedEnumerator.MoveNext())
  323. {
  324. current = appendedEnumerator.Current;
  325. return true;
  326. }
  327. break;
  328. }
  329. break;
  330. }
  331. Dispose();
  332. return false;
  333. }
  334. public override AppendPrepentAsyncIterator<TSource> Append(TSource item)
  335. {
  336. return new AppendPrependNAsyncIterator<TSource>(source, prepended, appended != null ? appended.Add(item) : new SingleLinkedNode<TSource>(item));
  337. }
  338. public override AppendPrepentAsyncIterator<TSource> Prepend(TSource item)
  339. {
  340. return new AppendPrependNAsyncIterator<TSource>(source, prepended != null ? prepended.Add(item) : new SingleLinkedNode<TSource>(item), appended);
  341. }
  342. public override async Task<TSource[]> ToArrayAsync(CancellationToken cancellationToken)
  343. {
  344. var count = await GetCountAsync(onlyIfCheap: true, cancellationToken: cancellationToken).ConfigureAwait(false);
  345. if (count == -1)
  346. {
  347. return await AsyncEnumerableHelpers.ToArray(this, cancellationToken).ConfigureAwait(false);
  348. }
  349. var array = new TSource[count];
  350. var index = 0;
  351. for (var n = prepended; n != null; n = n.Linked)
  352. {
  353. array[index] = n.Item;
  354. ++index;
  355. }
  356. var sourceCollection = source as ICollection<TSource>;
  357. if (sourceCollection != null)
  358. {
  359. sourceCollection.CopyTo(array, index);
  360. }
  361. else
  362. {
  363. using (var en = source.GetEnumerator())
  364. {
  365. while (await en.MoveNext(cancellationToken)
  366. .ConfigureAwait(false))
  367. {
  368. array[index] = en.Current;
  369. ++index;
  370. }
  371. }
  372. }
  373. index = array.Length;
  374. for (var n = appended; n != null; n = n.Linked)
  375. {
  376. --index;
  377. array[index] = n.Item;
  378. }
  379. return array;
  380. }
  381. public override async Task<List<TSource>> ToListAsync(CancellationToken cancellationToken)
  382. {
  383. var count = await GetCountAsync(onlyIfCheap: true, cancellationToken: cancellationToken).ConfigureAwait(false);
  384. var list = count == -1 ? new List<TSource>() : new List<TSource>(count);
  385. for (var n = prepended; n != null; n = n.Linked)
  386. {
  387. list.Add(n.Item);
  388. }
  389. using (var en = source.GetEnumerator())
  390. {
  391. while (await en.MoveNext(cancellationToken)
  392. .ConfigureAwait(false))
  393. {
  394. list.Add(en.Current);
  395. }
  396. }
  397. if (appended != null)
  398. {
  399. using (var en = appended.GetEnumerator())
  400. {
  401. while (en.MoveNext())
  402. {
  403. list.Add(en.Current);
  404. }
  405. }
  406. }
  407. return list;
  408. }
  409. public override async Task<int> GetCountAsync(bool onlyIfCheap, CancellationToken cancellationToken)
  410. {
  411. var listProv = source as IIListProvider<TSource>;
  412. if (listProv != null)
  413. {
  414. var count = await listProv.GetCountAsync(onlyIfCheap, cancellationToken).ConfigureAwait(false);
  415. return count == -1 ? -1 : count + (appended == null ? 0 : appended.Count) + (prepended == null ? 0 : prepended.Count);
  416. }
  417. return !onlyIfCheap || source is ICollection<TSource> ? await source.Count(cancellationToken).ConfigureAwait(false) + (appended == null ? 0 : appended.Count) + (prepended == null ? 0 : prepended.Count) : -1;
  418. }
  419. }
  420. }
  421. }