/// <summary> /// Creates a new <see cref="IAsyncReadOnlyCollection{T}" /> that contains the sorted elements of the /// <see cref="IAsyncEnumerable{T}" />. /// </summary> /// <param name="source"> /// The <see cref="IAsyncEnumerable{T}" /> to sort. /// </param> /// <param name="comparison"> /// The comparison to use while sorting. /// </param> /// <typeparam name="T"> /// The type of the items. /// </typeparam> /// <returns> /// The sorted <see cref="IAsyncReadOnlyCollection{T}" />. /// </returns> public async Task <IAsyncReadOnlyCollection <T> > SortAsync <T>(IAsyncEnumerable <T> source, Comparison <T> comparison) { var chunks = new List <IAsyncReadOnlyCollection <T> >(); if (source.Policy != this) { source = await this.MaterializeAsync(source).ConfigureAwait(false); } await source.Batch(this.MaximumChunkSize).ForEachAsync( async batch => { var builder = await this.CreateBuilder <T>().AddAsync(await this.memPolicy.SortAsync(batch, comparison).ConfigureAwait(false)); chunks.Add(await builder.BuildAsync().ConfigureAwait(false)); }); var result = this.CreateBuilder <T>(); var enumerators = chunks.Select(c => c.GetAsyncEnumerator()).ToList(); foreach (var enumerator in enumerators.ToArray()) { if (!enumerator.MoveNext() && !(await enumerator.NextBatchAsync().ConfigureAwait(false) && enumerator.MoveNext())) { enumerator.Dispose(); enumerators.Remove(enumerator); } } enumerators.Sort((a, b) => comparison(a.Current, b.Current)); while (true) { await result.AddAsync(TemporaryFilePolicy.TakeItems(enumerators, comparison)).ConfigureAwait(false); if (!(await enumerators[0].NextBatchAsync().ConfigureAwait(false) && enumerators[0].MoveNext())) { enumerators[0].Dispose(); enumerators.RemoveAt(0); if (enumerators.Count == 0) { break; } } else { TemporaryFilePolicy.SortEnumerators(enumerators, comparison); } } foreach (var chunk in chunks) { chunk.Dispose(); } return(await result.BuildAsync().ConfigureAwait(false)); }
/// <summary> /// Initializes a new instance of the <see cref="Collection{T}"/> class. /// </summary> /// <param name="policy"> /// The policy. /// </param> /// <param name="fileId"> /// The file id. /// </param> /// <param name="count"> /// The count. /// </param> /// <param name="transform"> /// The transform. /// </param> /// <param name="context"> /// The context. /// </param> public Collection(TemporaryFilePolicy policy, int fileId, long count, ITransform <T> transform, IDisposable context) { this.policy = policy; this.fileId = fileId; this.Count = count; this.transform = transform; this.context = context; }
/// <summary> /// Takes sorted items from the enumerators while it can be done synchronously. /// </summary> /// <param name="enumerators"> /// The enumerators. /// </param> /// <param name="comparison"> /// The comparison. /// </param> /// <typeparam name="T"> /// The type of the items. /// </typeparam> /// <returns> /// The items that were taken synchronously. /// </returns> private static IEnumerable <T> TakeItems <T>([NotNull] List <IAsyncEnumerator <T> > enumerators, Comparison <T> comparison) { while (true) { yield return(enumerators[0].Current); if (!enumerators[0].MoveNext()) { yield break; } TemporaryFilePolicy.SortEnumerators(enumerators, comparison); } }
public async Task <IAsyncReadOnlyCollection <T> > BuildAsync() { if (this.policy == null) { throw new InvalidOperationException("A builder can only be used once; collection was already built."); } if (this.addTask != null) { await this.addTask; } await this.file.FlushAsync(); this.file.Dispose(); this.file = null; var result = new Collection <T>(this.policy, this.fileId, this.count, this.transform, this.context); this.policy = null; return(result); }
/// <summary> /// Initializes a new instance of the <see cref="Builder{T}" /> class. /// </summary> /// <param name="policy"> /// The policy. /// </param> public Builder(TemporaryFilePolicy policy) { this.policy = policy; }