protected StorageWriteStream(
            long position,
            long bufferSize,
            IProgress <long> progressHandler,
            PooledMemoryStream buffer = null)
        {
            _position   = position;
            _bufferSize = bufferSize;

            if (progressHandler != null)
            {
                _progressHandler = new AggregatingProgressIncrementer(progressHandler);
            }

            if (buffer != null)
            {
                _buffer = buffer;
                _shouldDisposeBuffer = false;
            }
            else
            {
                _buffer = new PooledMemoryStream(
                    arrayPool: ArrayPool <byte> .Shared,
                    absolutePosition: 0,
                    maxArraySize: (int)Math.Min(Constants.MB, bufferSize));
                _shouldDisposeBuffer = true;
            }
        }
Example #2
0
        protected StorageWriteStream(
            long position,
            long bufferSize,
            IProgress <long> progressHandler,
            // TODO #27253
            //UploadTransactionalHashingOptions hashingOptions,
            PooledMemoryStream buffer = null)
        {
            _position   = position;
            _bufferSize = bufferSize;

            if (progressHandler != null)
            {
                _progressHandler = new AggregatingProgressIncrementer(progressHandler);
            }

            // TODO #27253
            //_hashingOptions = hashingOptions;
            // write streams don't support pre-calculated hashes
            //if (_hashingOptions?.PrecalculatedHash != default)
            //{
            //    throw Errors.PrecalculatedHashNotSupportedOnSplit();
            //}

            if (buffer != null)
            {
                _buffer = buffer;
                _shouldDisposeBuffer = false;
            }
            else
            {
                _buffer = new PooledMemoryStream(
                    arrayPool: ArrayPool <byte> .Shared,
                    absolutePosition: 0,
                    maxArraySize: (int)Math.Min(Constants.MB, bufferSize));
                _shouldDisposeBuffer = true;
            }
        }
        /// <summary>
        /// Buffers a portion of the given stream, returning the buffered stream partition.
        /// </summary>
        /// <param name="stream">
        /// Stream to buffer from.
        /// </param>
        /// <param name="minCount">
        /// Minimum number of bytes to buffer. This method will not return until at least this many bytes have been read from <paramref name="stream"/> or the stream completes.
        /// </param>
        /// <param name="maxCount">
        /// Maximum number of bytes to buffer.
        /// </param>
        /// <param name="absolutePosition">
        /// Current position of the stream, since <see cref="Stream.Position"/> throws if not seekable.
        /// </param>
        /// <param name="arrayPool">
        /// Pool to rent buffer space from.
        /// </param>
        /// <param name="maxArrayPoolRentalSize">
        /// Max size we can request from the array pool.
        /// </param>
        /// <param name="async">
        /// Whether to perform this operation asynchronously.
        /// </param>
        /// <param name="cancellationToken">
        /// Cancellation token.
        /// </param>
        /// <returns>
        /// The buffered stream partition with memory backed by an array pool.
        /// </returns>
        internal static async Task <PooledMemoryStream> BufferStreamPartitionInternal(
            Stream stream,
            long minCount,
            long maxCount,
            long absolutePosition,
            ArrayPool <byte> arrayPool,
            int?maxArrayPoolRentalSize,
            bool async,
            CancellationToken cancellationToken)
        {
            long totalRead       = 0;
            var  streamPartition = new PooledMemoryStream(arrayPool, absolutePosition, maxArrayPoolRentalSize ?? DefaultMaxArrayPoolRentalSize);

            // max count to write into a single array
            int maxCountIndividualBuffer;
            // min count to write into a single array
            int minCountIndividualBuffer;
            // the amount that was written into the current array
            int readIndividualBuffer;

            do
            {
                // buffer to write to
                byte[] buffer;
                // offset to start writing at
                int             offset;
                BufferPartition latestBuffer = streamPartition.GetLatestBufferWithAvailableSpaceOrDefault();
                // whether we got a brand new buffer to write into
                bool newbuffer;
                if (latestBuffer != default)
                {
                    buffer    = latestBuffer.Buffer;
                    offset    = latestBuffer.DataLength;
                    newbuffer = false;
                }
                else
                {
                    buffer    = arrayPool.Rent((int)Math.Min(maxCount - totalRead, streamPartition.MaxArraySize));
                    offset    = 0;
                    newbuffer = true;
                }

                // limit max and min count for this buffer by buffer length
                maxCountIndividualBuffer = (int)Math.Min(maxCount - totalRead, buffer.Length - offset);
                // definitionally limited by max; we won't ever have a swapped min/max range
                minCountIndividualBuffer = (int)Math.Min(minCount - totalRead, maxCountIndividualBuffer);

                readIndividualBuffer = await ReadLoopInternal(
                    stream,
                    buffer,
                    offset : offset,
                    minCountIndividualBuffer,
                    maxCountIndividualBuffer,
                    async,
                    cancellationToken).ConfigureAwait(false);

                // if nothing was placed in a brand new array
                if (readIndividualBuffer == 0 && newbuffer)
                {
                    arrayPool.Return(buffer);
                }
                // if brand new array and we did place data in it
                else if (newbuffer)
                {
                    streamPartition.BufferSet.Add(new BufferPartition
                    {
                        Buffer     = buffer,
                        DataLength = readIndividualBuffer
                    });
                }
                // added to an existing array that was not entirely filled
                else
                {
                    latestBuffer.DataLength += readIndividualBuffer;
                }

                totalRead += readIndividualBuffer;

                /* If we filled the buffer this loop, then quitting on min count is pointless. The point of quitting
                 * on min count is when the source stream doesn't have available bytes and we've reached an amount worth
                 * sending instead of blocking on. If we filled the available array, we don't actually know whether more
                 * data is available yet, as we limited our read for reasons outside the stream state. We should therefore
                 * try another read regardless of whether we hit min count.
                 */
            } while (
                // stream is done if this value is zero; no other check matters
                readIndividualBuffer != 0 &&
                // stop filling the partition if we've hit the max size of the partition
                totalRead < maxCount &&
                // stop filling the partition if we've reached min count and we know we've hit at least a pause in the stream
                (totalRead < minCount || readIndividualBuffer == maxCountIndividualBuffer));

            return(streamPartition);
        }