/// <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
            }));
        }
Пример #3
0
        /// <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);
                }
            }
        }
Пример #4
0
        /// <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));
                }
            }
        }