/// <summary> /// Upload a single block. This can happen on parallel threads. /// </summary> /// <param name="blockIdSequenceNumber">The block sequence prefix value.</param> /// <param name="setResult">The set result.</param> /// <returns>A <see cref="TaskSequence"/> that dispenses a block stream.</returns> private TaskSequence DispenseBlockStream(long blockIdSequenceNumber, Action <SmallBlockMemoryStream, string, string> setResult) { int currentCallIndex = this.dispenserCallCount++; TraceHelper.WriteLine("Staring dispensBlockStream for id {0}", currentCallIndex); SmallBlockMemoryStream memoryStream = new SmallBlockMemoryStream(Constants.DefaultBufferSize); var md5Check = MD5.Create(); int totalCopied = 0, numRead = 0; do { byte[] buffer = new byte[Constants.DefaultBufferSize]; var numToRead = (int)Math.Min(buffer.Length, this.blockSize - totalCopied); var readTask = this.sourceStream.ReadAsync(buffer, 0, numToRead); yield return(readTask); numRead = readTask.Result; if (numRead != 0) { // Verify the content StreamUtilities.ComputeHash(buffer, 0, numRead, md5Check); StreamUtilities.ComputeHash(buffer, 0, numRead, this.blobHash); var writeTask = memoryStream.WriteAsync(buffer, 0, numRead); yield return(writeTask); // Materialize any exceptions var scratch = writeTask.Result; Console.WriteLine(scratch); totalCopied += numRead; } }while (numRead != 0 && totalCopied < this.blockSize); // No locking necessary as only once active Dispense Task this.dispensizedStreamSize += totalCopied; if (totalCopied != 0) { string hashVal = StreamUtilities.GetHashValue(md5Check); string blockId = Utilities.GenerateBlockIDWithHash(hashVal, blockIdSequenceNumber); this.blockList.Add(blockId); memoryStream.Position = 0; setResult(memoryStream, blockId, hashVal); } else { memoryStream.Close(); setResult(null, null, null); } TraceHelper.WriteLine("Ending dispensBlockStream for id {0}", currentCallIndex); }
private SynchronousTask WriteNonBlockedBlobImpl(byte[] buffer, int offset, int count) { return(new SynchronousTask(() => { if (this.blockBuffer == null) { this.CreateNewBlock(); } // Verify we don't go beyond single-upload size if (this.Length + count > Protocol.Constants.MaxSingleUploadBlobSize) { throw new ArgumentOutOfRangeException("count", String.Format(CultureInfo.CurrentCulture, SR.BlobTooLargeError, Protocol.Constants.MaxSingleUploadBlobSize)); } this.blockBuffer.Write(buffer, offset, count); this.position += count; StreamUtilities.ComputeHash(buffer, offset, count, this.blockHash); StreamUtilities.ComputeHash(buffer, offset, count, this.blobHash); // Update both, in case someone changes their mind mid-stream })); }
/// <summary> /// Implements the block writing task. /// </summary> /// <param name="buffer">The buffer to write data from.</param> /// <param name="offset">The byte offset in buffer from which to begin writing.</param> /// <param name="count">The number of bytes to write.</param> /// <returns>The sequence representing the uploading of the blocks.</returns> private TaskSequence WriteBlockBlobImpl(byte[] buffer, int offset, int count) { if (this.blockList.Count + (count / this.blockSize) > Constants.MaxBlockNumber) { throw new ArgumentOutOfRangeException("count", String.Format(CultureInfo.CurrentCulture, SR.TooManyBlocksError, Constants.MaxBlockNumber)); } while (count != 0) { // Create a buffer if we don't have one if (this.blockBuffer == null) { this.CreateNewBlock(); } // Copy enough data to fill the buffer. int numCopied = (int)Math.Min(count, this.blockSize - this.blockBuffer.Length); this.blockBuffer.Write(buffer, offset, numCopied); StreamUtilities.ComputeHash(buffer, offset, numCopied, this.blockHash); StreamUtilities.ComputeHash(buffer, offset, numCopied, this.blobHash); // Advance the location this.position += numCopied; offset += numCopied; count -= numCopied; // If buffer is full, flush if (this.blockBuffer.Length == this.blockSize) { var newTask = new InvokeTaskSequenceTask(this.FlushInternal); yield return(newTask); // Make sure task completed successfully by materializing the exception var result = newTask.Result; Console.WriteLine(result); } } }
/// <summary> /// Reads the data from the service starting at the specified location. Verifies the block's signature (if required) and adds it to the buffered data. /// </summary> /// <param name="startPosition">The starting position of the read-ahead.</param> /// <param name="length">The number of bytes to read ahead.</param> /// <returns> An TaskSequence that represents the asynchronous read action. </returns> private TaskSequence ReadAheadImpl(long startPosition, long length) { var webResponseTask = new InvokeTaskSequenceTask <Stream>((result) => { return(this.Blob.GetStreamImpl(options, startPosition, length, result)); }); yield return(webResponseTask); using (var stream = webResponseTask.Result) { this.LockToEtag(); if (this.IntegrityControlVerificationEnabled && this.Blob.Properties.BlobType == BlobType.BlockBlob) { long blockStartPosition = 0; foreach (var block in this.blockList) { var blockSize = block.Size; // Find the starting block if (blockStartPosition < startPosition) { blockStartPosition += blockSize; continue; } // Start creating blocks var memoryStream = new SmallBlockMemoryStream(Constants.DefaultBufferSize); var md5Check = MD5.Create(); int totalCopied = 0, numRead = 0; do { byte[] buffer = new byte[Constants.DefaultBufferSize]; var numToRead = (int)Math.Min(buffer.Length, blockSize - totalCopied); var readTask = stream.ReadAsync(buffer, 0, numToRead); yield return(readTask); numRead = readTask.Result; if (numRead != 0) { // Verify the content StreamUtilities.ComputeHash(buffer, 0, numRead, md5Check); var writeTask = memoryStream.WriteAsync(buffer, 0, numRead); yield return(writeTask); var scratch = writeTask.Result; // Materialize any exceptions totalCopied += numRead; } }while (numRead != 0 && totalCopied < blockSize); // If we read something, act on it if (totalCopied != 0) { // Verify the hash string blockNameMD5Value = Utilities.ExtractMD5ValueFromBlockID(block.Name); if (blockNameMD5Value != StreamUtilities.GetHashValue(md5Check)) { throw new InvalidDataException("Blob data corrupted (integrity check failed)"); } memoryStream.Position = 0; // Rewind the stream to allow for reading this.downloadedBlocksList.Add(new DownloadedBlock(startPosition, memoryStream)); startPosition += blockSize; blockStartPosition += blockSize; } else { break; } } } else { var memoryStream = new SmallBlockMemoryStream(Constants.DefaultBufferSize); var copyTask = new InvokeTaskSequenceTask(() => { return(stream.WriteTo(memoryStream)); }); yield return(copyTask); var scratch = copyTask.Result; // Materialize any errors memoryStream.Position = 0; // Rewind the stream to allow for reading this.downloadedBlocksList.Add(new DownloadedBlock(startPosition, memoryStream)); } } }