Example #1
0
        // In order:
        //
        //   * If there are no chunks with the starting file position in [from, to), returns null.
        //   * Else if the predicate evaluates to true on the first chunk, returns the first chunk.
        //   * Else returns a chunk for which the predicate evaluates to false and either it's the
        //     last chunk or the predicate evaluates to true on the next chunk. If there are multiple
        //     chunks satisfying these requirements, it's unspecified which one is returned.
        //
        // Implication: If false chunks cannot follow true chunks, returns the last false chunk if
        // there are any or the very first chunk otherwise.
        //
        // There are no constraints on the values of position boundaries. Even long.MinValue and
        // long.MaxValue are legal. If from >= to, the result is null.
        public async Task <InputChunk> ReadAtPartitionAsync(long from, long to, Func <UserData, bool> pred)
        {
            if (pred == null)
            {
                throw new ArgumentNullException(nameof(pred));
            }
            IChunk left = await _reader.ReadFirstAsync(from, to);

            if (left == null || pred.Invoke(left.UserData))
            {
                return(await MakeChunk(left, Scan.Forward, from, to));
            }
            IChunk right = await _reader.ReadLastAsync(left.BeginPosition + 1, to);

            if (right == null)
            {
                return(await MakeChunk(left, Scan.None, from, to));
            }
            if (!pred.Invoke(right.UserData))
            {
                return(await MakeChunk(right, Scan.Backward, from, to));
            }
            while (true)
            {
                IChunk mid = await _reader.ReadMiddleAsync(left.BeginPosition + 1, right.BeginPosition);

                if (mid == null)
                {
                    return(await MakeChunk(left, Scan.Backward, from, to) ?? await MakeChunk(left, Scan.Forward, from, to));
                }
                if (pred.Invoke(mid.UserData))
                {
                    right = mid;
                }
                else
                {
                    left = mid;
                }
            }
        }