< Summary

Class:Azure.Storage.Shared.WindowStream
Assembly:Azure.Storage.Files.DataLake
File(s):C:\Git\azure-sdk-for-net\sdk\storage\Azure.Storage.Common\src\Shared\WindowStream.cs
Covered lines:29
Uncovered lines:31
Coverable lines:60
Total lines:229
Line coverage:48.3% (29 of 60)
Covered branches:4
Total branches:14
Branch coverage:28.5% (4 of 14)

Metrics

MethodCyclomatic complexity Line coverage Branch coverage
get_InnerStream()-100%100%
get_AbsolutePosition()-100%100%
get_CanRead()-100%100%
get_CanWrite()-0%100%
.ctor(...)-100%100%
GetWindow(...)-66.67%50%
Flush()-0%100%
Write(...)-0%100%
WriteByte(...)-0%100%
ReadByte()-0%0%
Read(...)-100%100%
ReadAsync()-0%100%
ReadInternal()-100%75%
get_MaxLength()-0%100%
.ctor(...)-0%100%
get_CanSeek()-0%100%
get_Length()-0%100%
get_Position()-0%100%
Seek(...)-0%100%
SetLength(...)-0%100%
AdjustCount(...)-0%100%
ReportInnerStreamRead(...)-0%100%
.ctor(...)-100%100%
get_CanSeek()-100%100%
get_Length()-100%100%
get_Position()-100%100%
set_Position(...)-0%100%
Seek(...)-0%0%
SetLength(...)-0%100%
AdjustCount(...)-100%100%
ReportInnerStreamRead(...)-100%100%

File(s)

C:\Git\azure-sdk-for-net\sdk\storage\Azure.Storage.Common\src\Shared\WindowStream.cs

#LineLine coverage
 1// Copyright (c) Microsoft Corporation. All rights reserved.
 2// Licensed under the MIT License.
 3
 4using System;
 5using System.IO;
 6using System.Threading;
 7using System.Threading.Tasks;
 8using Azure.Core.Pipeline;
 9
 10#pragma warning disable SA1402  // File may only contain a single type
 11// branching logic on wrapping seekable vs unseekable streams has been handled via inheritance
 12
 13namespace Azure.Storage.Shared
 14{
 15    /// <summary>
 16    /// Exposes a predetermined slice of a larger stream using the same Stream interface.
 17    /// There should not be access to the base stream while this facade is in use.
 18    /// </summary>
 19    internal abstract class WindowStream : SlicedStream
 20    {
 1621        private Stream InnerStream { get; }
 22
 2023        public override long AbsolutePosition { get; }
 24
 425        public override bool CanRead => true;
 26
 027        public override bool CanWrite => false;
 28
 29        /// <summary>
 30        /// Constructs a window of an underlying stream.
 31        /// </summary>
 32        /// <param name="stream">
 33        /// Potentialy unseekable stream to expose a window of.
 34        /// </param>
 35        /// <param name="absolutePosition">
 36        /// The offset of this stream from the start of the wrapped stream.
 37        /// </param>
 638        private WindowStream(Stream stream, long absolutePosition)
 39        {
 640            InnerStream = stream;
 641            AbsolutePosition = absolutePosition;
 642        }
 43
 44        public static WindowStream GetWindow(Stream stream, long maxWindowLength, long absolutePosition = default)
 45        {
 646            if (stream.CanSeek)
 47            {
 648                return new SeekableWindowStream(stream, maxWindowLength);
 49            }
 50            else
 51            {
 052                return new UnseekableWindowStream(stream, maxWindowLength, absolutePosition);
 53            }
 54        }
 55
 56        public override void Flush()
 57        {
 058            throw new NotSupportedException();
 59        }
 60
 061        public override void Write(byte[] buffer, int offset, int count) => throw new NotSupportedException();
 62
 063        public override void WriteByte(byte value) => throw new NotSupportedException();
 64
 65        /// <inheritdoc/>
 66        /// <remarks>
 67        /// Implementing this method takes advantage of any optimizations in the wrapped stream's implementation.
 68        /// </remarks>
 69        public override int ReadByte()
 70        {
 071            if (AdjustCount(1) <= 0)
 72            {
 073                return -1;
 74            }
 75
 076            int val = InnerStream.ReadByte();
 077            if (val != -1)
 78            {
 079                ReportInnerStreamRead(1);
 80            }
 81
 082            return val;
 83        }
 84
 85        public override int Read(byte[] buffer, int offset, int count)
 886                => ReadInternal(buffer, offset, count, async: false, cancellationToken: default).EnsureCompleted();
 87
 88        public override async Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationTo
 089            => await ReadInternal(buffer, offset, count, async: true, cancellationToken).ConfigureAwait(false);
 90
 91        private async Task<int> ReadInternal(byte[] buffer, int offset, int count, bool async, CancellationToken cancell
 92        {
 893            count = AdjustCount(count);
 894            if (count <= 0)
 95            {
 496                return 0;
 97            }
 98
 499            int result = async
 4100                ? await InnerStream.ReadAsync(buffer, offset, count, cancellationToken).ConfigureAwait(false)
 4101                : InnerStream.Read(buffer, offset, count);
 102
 4103            ReportInnerStreamRead(result);
 4104            return result;
 8105        }
 106
 107        protected abstract int AdjustCount(int count);
 108
 109        protected abstract void ReportInnerStreamRead(int resultRead);
 110
 111        /// <summary>
 112        /// Exposes a predetermined slice of a larger, unseekable stream using the same Stream
 113        /// interface. There should not be access to the base stream while this facade is in use.
 114        /// This stream wrapper is unseekable. To wrap a partition of an unseekable stream where
 115        /// the partition is seekable, see <see cref="PooledMemoryStream"/>.
 116        /// </summary>
 117        private class UnseekableWindowStream : WindowStream
 118        {
 119            private long _position = 0;
 120
 0121            private long MaxLength { get; }
 122
 0123            public UnseekableWindowStream(Stream stream, long maxWindowLength, long absolutePosition) : base(stream, abs
 124            {
 0125                MaxLength = maxWindowLength;
 0126            }
 127
 0128            public override bool CanSeek => false;
 129
 0130            public override long Length => throw new NotSupportedException();
 131
 0132            public override long Position { get => throw new NotSupportedException(); set => throw new NotSupportedExcep
 133
 0134            public override long Seek(long offset, SeekOrigin origin) => throw new NotSupportedException();
 135
 0136            public override void SetLength(long value) => throw new NotSupportedException();
 137
 138            protected override int AdjustCount(int count)
 0139                => (int)Math.Min(count, MaxLength - _position);
 140
 141            protected override void ReportInnerStreamRead(int resultRead)
 0142                => _position += resultRead;
 143        }
 144
 145        /// <summary>
 146        /// Exposes a predetermined slice of a larger, seekable stream using the same Stream
 147        /// interface. There should not be access to the base stream while this facade is in use.
 148        /// This stream wrapper is sseekable. To wrap a partition of an unseekable stream where
 149        /// the partition is seekable, see <see cref="PooledMemoryStream"/>.
 150        /// </summary>
 151        private class SeekableWindowStream : WindowStream
 152        {
 6153            public SeekableWindowStream(Stream stream, long maxWindowLength) : base(stream, stream.Position)
 154            {
 155                // accessing the stream's Position in the constructor acts as our validator that we're wrapping a seekab
 6156                Length = Math.Min(
 6157                    stream.Length - stream.Position,
 6158                    maxWindowLength);
 6159            }
 160
 4161            public override bool CanSeek => true;
 162
 36163            public override long Length { get; }
 164
 165            public override long Position
 166            {
 12167                get => InnerStream.Position - AbsolutePosition;
 0168                set => InnerStream.Position = AbsolutePosition + value;
 169            }
 170
 171            public override long Seek(long offset, SeekOrigin origin)
 172            {
 173                switch (origin)
 174                {
 175                    case SeekOrigin.Begin:
 0176                        InnerStream.Seek(AbsolutePosition + offset, SeekOrigin.Begin);
 0177                        break;
 178                    case SeekOrigin.Current:
 0179                        InnerStream.Seek(InnerStream.Position + offset, SeekOrigin.Current);
 0180                        break;
 181                    case SeekOrigin.End:
 0182                        InnerStream.Seek((AbsolutePosition + this.Length) - InnerStream.Length + offset, SeekOrigin.End)
 183                        break;
 184                }
 0185                return Position;
 186            }
 187
 0188            public override void SetLength(long value) => throw new NotSupportedException();
 189
 190            protected override int AdjustCount(int count)
 8191                => (int)Math.Min(count, Length - Position);
 192
 193            protected override void ReportInnerStreamRead(int resultRead)
 194            {
 195                // no-op, inner stream took care of position adjustment
 4196            }
 197        }
 198    }
 199
 200    internal static partial class StreamExtensions
 201    {
 202        /// <summary>
 203        /// Some streams will throw if you try to access their length so we wrap
 204        /// the check in a TryGet helper.
 205        /// </summary>
 206        public static long? GetPositionOrDefault(this Stream content)
 207        {
 208            if (content == null)
 209            {
 210                /* Returning 0 instead of default puts us on the quick and clean one-shot upload,
 211                 * which produces more consistent fail state with how a 1-1 method on the convenience
 212                 * layer would fail.
 213                 */
 214                return 0;
 215            }
 216            try
 217            {
 218                if (content.CanSeek)
 219                {
 220                    return content.Position;
 221                }
 222            }
 223            catch (NotSupportedException)
 224            {
 225            }
 226            return default;
 227        }
 228    }
 229}