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; } }
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); }