< Summary

Class:Azure.Storage.LazyLoadingReadOnlyStream`2
Assembly:Azure.Storage.Files.Shares
File(s):C:\Git\azure-sdk-for-net\sdk\storage\Azure.Storage.Common\src\Shared\LazyLoadingReadOnlyStream.cs
Covered lines:66
Uncovered lines:39
Coverable lines:105
Total lines:298
Line coverage:62.8% (66 of 105)
Covered branches:21
Total branches:34
Branch coverage:61.7% (21 of 34)

Metrics

MethodCyclomatic complexity Line coverage Branch coverage
.ctor(...)-100%100%
Read(...)-0%100%
ReadAsync()-100%100%
ReadInternal()-64.71%50%
DownloadInternal()-75%50%
ValidateReadParameters(...)-100%100%
Dispose(...)-0%0%
GetBlobLengthInternal()-0%0%
GetBlobLengthFromResponse(...)-80%50%
get_CanRead()-0%100%
get_CanSeek()-0%100%
get_CanWrite()-0%100%
get_Length()-100%100%
get_Position()-0%100%
set_Position(...)-0%100%
Seek(...)-0%100%
SetLength(...)-0%100%
Write(...)-0%100%
Flush()-0%100%
FlushAsync(...)-0%100%

File(s)

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

#LineLine coverage
 1// Copyright (c) Microsoft Corporation. All rights reserved.
 2// Licensed under the MIT License.
 3
 4using System;
 5using System.Buffers;
 6using System.Globalization;
 7using System.IO;
 8using System.Threading;
 9using System.Threading.Tasks;
 10using Azure.Core;
 11using Azure.Core.Pipeline;
 12using Azure.Storage.Shared;
 13
 14namespace Azure.Storage
 15{
 16    /// <summary>
 17    /// Used for Open Read APIs.
 18    /// </summary>
 19    internal class LazyLoadingReadOnlyStream<TRequestConditions, TProperties> : Stream
 20    {
 21        /// <summary>
 22        /// The current position within the blob or file.
 23        /// </summary>
 24        private long _position;
 25
 26        /// <summary>
 27        /// Last known length of underlying blob or file.
 28        /// </summary>
 29        private long _length;
 30
 31        /// <summary>
 32        /// The number of bytes to download per call.
 33        /// </summary>
 34        private readonly int _bufferSize;
 35
 36        /// <summary>
 37        /// The backing buffer.
 38        /// </summary>
 39        private byte[] _buffer;
 40
 41        /// <summary>
 42        /// The current position within the buffer.
 43        /// </summary>
 44        private int _bufferPosition;
 45
 46        /// <summary>
 47        /// The current length of the buffer that is populated.
 48        /// </summary>
 49        private int _bufferLength;
 50
 51        /// <summary>
 52        /// If we are allowing the blob to be modifed while we read it.
 53        /// </summary>
 54        private bool _allowBlobModifications;
 55
 56        /// <summary>
 57        /// Request conditions to send on the download requests.
 58        /// </summary>
 59        private TRequestConditions _requestConditions;
 60
 61        /// <summary>
 62        /// Download() function.
 63        /// </summary>
 64        private readonly Func<HttpRange, TRequestConditions, bool, bool, CancellationToken, Task<Response<IDownloadedCon
 65
 66        /// <summary>
 67        /// Function to create RequestConditions.
 68        /// </summary>
 69        private readonly Func<ETag?, TRequestConditions> _createRequestConditionsFunc;
 70
 71        /// <summary>
 72        /// Function to get properties.
 73        /// </summary>
 74        private readonly Func<bool, CancellationToken, Task<Response<TProperties>>> _getPropertiesInternalFunc;
 75
 1476        public LazyLoadingReadOnlyStream(
 1477            Func<HttpRange, TRequestConditions, bool, bool, CancellationToken, Task<Response<IDownloadedContent>>> downl
 1478            Func<ETag?, TRequestConditions> createRequestConditionsFunc,
 1479            Func<bool, CancellationToken, Task<Response<TProperties>>> getPropertiesFunc,
 1480            long position = 0,
 1481            int? bufferSize = default,
 1482            TRequestConditions requestConditions = default)
 83        {
 1484            _downloadInternalFunc = downloadInternalFunc;
 1485            _createRequestConditionsFunc = createRequestConditionsFunc;
 1486            _getPropertiesInternalFunc = getPropertiesFunc;
 1487            _position = position;
 1488            _bufferSize = bufferSize ?? Constants.DefaultStreamingDownloadSize;
 1489            _buffer = ArrayPool<byte>.Shared.Rent(_bufferSize);
 1490            _bufferPosition = 0;
 1491            _bufferLength = 0;
 1492            _requestConditions = requestConditions;
 1493            _length = -1;
 1494            _allowBlobModifications = !(_requestConditions == null && _createRequestConditionsFunc != null);
 1495        }
 96
 97        public override int Read(byte[] buffer, int offset, int count)
 098            => ReadInternal(
 099                buffer,
 0100                offset,
 0101                count,
 0102                async: false,
 0103                default)
 0104            .EnsureCompleted();
 105
 106        public override async Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationTo
 40107            => await ReadInternal(
 40108                buffer,
 40109                offset,
 40110                count,
 40111                async: true,
 40112                cancellationToken)
 40113                .ConfigureAwait(false);
 114
 115        public async Task<int> ReadInternal(byte[] buffer, int offset, int count, bool async, CancellationToken cancella
 116        {
 40117            ValidateReadParameters(buffer, offset, count);
 118
 32119            if (_position == _length)
 120            {
 0121                if (_allowBlobModifications)
 122                {
 123                    // In case the blob grow since our last download call.
 0124                    _length = await GetBlobLengthInternal(async, cancellationToken).ConfigureAwait(false);
 125
 0126                    if (_position == _length)
 127                    {
 0128                        return 0;
 129                    }
 130                }
 131                else
 132                {
 0133                    return 0;
 134                }
 135
 136            }
 137
 32138            if (_bufferPosition == 0 || _bufferPosition == _buffer.Length)
 139            {
 32140                int lastDownloadedBytes = await DownloadInternal(async, cancellationToken).ConfigureAwait(false);
 28141                if (lastDownloadedBytes == 0)
 142                {
 0143                    return 0;
 144                }
 145            }
 146
 28147            int remainingBytesInBuffer = _bufferLength - _bufferPosition;
 148
 149            // We will return the minimum of remainingBytesInBuffer and the count provided by the user
 28150            int bytesToWrite = Math.Min(remainingBytesInBuffer, count);
 151
 28152            Array.Copy(_buffer, _bufferPosition, buffer, offset, bytesToWrite);
 153
 28154            _position += bytesToWrite;
 155
 28156            return bytesToWrite;
 28157        }
 158
 159        private async Task<int> DownloadInternal(bool async, CancellationToken cancellationToken)
 160        {
 161            Response<IDownloadedContent> response;
 162
 32163            HttpRange range = new HttpRange(_position, _bufferSize);
 164
 165#pragma warning disable AZC0110 // DO NOT use await keyword in possibly synchronous scope.
 32166            response = await _downloadInternalFunc(range, _requestConditions, default, async, cancellationToken).Configu
 167#pragma warning restore AZC0110 // DO NOT use await keyword in possibly synchronous scope.
 168
 28169            using Stream networkStream = response.Value.Content;
 170
 171            int copiedBytes;
 172
 28173            if (async)
 174            {
 28175                copiedBytes = await networkStream.ReadAsync(
 28176                    buffer: _buffer,
 28177                    offset: 0,
 28178                    count: _buffer.Length,
 28179                    cancellationToken: cancellationToken).ConfigureAwait(false);
 180            }
 181            else
 182            {
 0183                copiedBytes = networkStream.Read(
 0184                    buffer: _buffer,
 0185                    offset: 0,
 0186                    count: _buffer.Length);
 187            }
 188
 28189            _bufferPosition = 0;
 28190            _bufferLength = copiedBytes;
 28191            _length = GetBlobLengthFromResponse(response.GetRawResponse());
 192
 193            // Set _requestConditions If-Match if we are not allowing the blob to be modified.
 28194            if (!_allowBlobModifications)
 195            {
 0196                _requestConditions = _createRequestConditionsFunc(response.GetRawResponse().Headers.ETag);
 197            }
 198
 28199            return response.GetRawResponse().Headers.ContentLength.GetValueOrDefault();
 28200        }
 201
 202        private static void ValidateReadParameters(byte[] buffer, int offset, int count)
 203        {
 40204            if (buffer == null)
 205            {
 2206                throw new ArgumentNullException($"{nameof(buffer)}", $"{nameof(buffer)} cannot be null.");
 207            }
 208
 38209            if (offset < 0)
 210            {
 2211                throw new ArgumentOutOfRangeException($"{nameof(offset)} cannot be less than 0.");
 212            }
 213
 36214            if (offset > buffer.Length)
 215            {
 2216                throw new ArgumentOutOfRangeException($"{nameof(offset)} cannot exceed {nameof(buffer)} length.");
 217            }
 218
 34219            if (count < 0)
 220            {
 2221                throw new ArgumentOutOfRangeException($"{nameof(count)} cannot be less than 0.");
 222            }
 32223        }
 224
 225        protected override void Dispose(bool disposing)
 226        {
 227            // Return the buffer to the pool if we're called from Dispose or a finalizer
 0228            if (_buffer != null)
 229            {
 0230                ArrayPool<byte>.Shared.Return(_buffer, clearArray: true);
 0231                _buffer = null;
 232            }
 0233        }
 234
 235        private async Task<long> GetBlobLengthInternal(bool async, CancellationToken cancellationToken)
 236        {
 237#pragma warning disable AZC0110 // DO NOT use await keyword in possibly synchronous scope.
 0238            Response<TProperties> response = await _getPropertiesInternalFunc(async, cancellationToken).ConfigureAwait(f
 239#pragma warning restore AZC0110 // DO NOT use await keyword in possibly synchronous scope.
 240
 0241            response.GetRawResponse().Headers.TryGetValue("Content-Length", out string lengthString);
 242
 0243            if (lengthString == null)
 244            {
 0245                throw new ArgumentException($"{HttpHeader.Names.ContentLength} header is mssing on get properties respon
 246            }
 247
 0248            return Convert.ToInt64(lengthString, CultureInfo.InvariantCulture);
 0249        }
 250
 251        private static long GetBlobLengthFromResponse(Response response)
 252        {
 28253            response.Headers.TryGetValue("Content-Range", out string lengthString);
 254
 28255            if (lengthString == null)
 256            {
 0257                throw new ArgumentException("Content-Range header is mssing on download response.");
 258            }
 259
 28260            string[] split = lengthString.Split('/');
 28261            return Convert.ToInt64(split[1], CultureInfo.InvariantCulture);
 262        }
 263
 0264        public override bool CanRead => true;
 265
 0266        public override bool CanSeek => false;
 267
 0268        public override bool CanWrite => false;
 269
 8270        public override long Length => _length;
 271
 272        public override long Position
 273        {
 0274            get => _position;
 0275            set => throw new NotSupportedException();
 276        }
 277
 278        public override long Seek(long offset, SeekOrigin origin)
 279        {
 0280            throw new NotSupportedException();
 281        }
 282
 283        public override void SetLength(long value)
 284        {
 0285            throw new NotSupportedException();
 286        }
 287
 288        public override void Write(byte[] buffer, int offset, int count)
 289        {
 0290            throw new NotSupportedException();
 291        }
 292
 0293        public override void Flush() { }
 294
 295        public override Task FlushAsync(CancellationToken cancellationToken)
 0296            => Task.CompletedTask;
 297    }
 298}