public SyncTransferController(
            TransferScheduler transferScheduler,
            TransferJob transferJob,
            CancellationToken userCancellationToken)
            : base(transferScheduler, transferJob, userCancellationToken)
        {
            if (null == transferScheduler)
            {
                throw new ArgumentNullException("transferScheduler");
            }

            if (null == transferJob)
            {
                throw new ArgumentNullException("transferJob");
            }

            this.SharedTransferData = new SharedTransferData()
            {
                TransferJob   = this.TransferJob,
                AvailableData = new ConcurrentDictionary <long, TransferData>(),
            };

            if (null == transferJob.CheckPoint)
            {
                transferJob.CheckPoint = new SingleObjectCheckpoint();
            }

            reader = this.GetReader(transferJob.Source);
            writer = this.GetWriter(transferJob.Destination);
        }
        public SyncTransferController(
            TransferScheduler transferScheduler,
            TransferJob transferJob,
            CancellationToken userCancellationToken)
            : base(transferScheduler, transferJob, userCancellationToken)
        {
            if (null == transferScheduler)
            {
                throw new ArgumentNullException("transferScheduler");
            }

            if (null == transferJob)
            {
                throw new ArgumentNullException("transferJob");
            }

            this.SharedTransferData = new SharedTransferData()
            {
                TransferJob = this.TransferJob,
                AvailableData = new ConcurrentDictionary<long, TransferData>(),
            };

            if (null == transferJob.CheckPoint)
            {
                transferJob.CheckPoint = new SingleObjectCheckpoint();
            }
            
            reader = this.GetReader(transferJob.Source);
            writer = this.GetWriter(transferJob.Destination);
        }
        public SyncTransferController(
            TransferScheduler transferScheduler,
            TransferJob transferJob,
            CancellationToken userCancellationToken)
            : base(transferScheduler, transferJob, userCancellationToken)
        {
            if (null == transferScheduler)
            {
                throw new ArgumentNullException(nameof(transferScheduler));
            }

            if (null == transferJob)
            {
                throw new ArgumentNullException(nameof(transferJob));
            }

            this.SharedTransferData = new SharedTransferData()
            {
                TransferJob   = this.TransferJob,
                AvailableData = new ConcurrentDictionary <long, TransferData>(),
            };

            if (null == transferJob.CheckPoint)
            {
                transferJob.CheckPoint = new SingleObjectCheckpoint();
            }

            this.reader = this.GetReader(transferJob.Source);
            this.writer = this.GetWriter(transferJob.Destination);

            this.CheckAndEnableSmallFileOptimization();

            this.SharedTransferData.OnTotalLengthChanged += (sender, args) =>
            {
                // For large block blob uploading, we need to re-calculate the BlockSize according to the total size
                // The formula: Ceiling(TotalSize / (50000 * DefaultBlockSize)) * DefaultBlockSize. This will make sure the
                // new block size will be mutiple of DefaultBlockSize(aka MemoryManager's chunk size)
                if (this.writer is BlockBlobWriter)
                {
                    var normalMaxBlockBlobSize = (long)50000 * Constants.DefaultBlockSize;

                    // Calculate the min block size according to the blob total length
                    var memoryChunksRequiredEachTime = (int)Math.Ceiling((double)this.SharedTransferData.TotalLength / normalMaxBlockBlobSize);
                    var blockSize = memoryChunksRequiredEachTime * Constants.DefaultBlockSize;

                    // Take the block size user specified when it's greater than the calculated value
                    if (TransferManager.Configurations.BlockSize > blockSize)
                    {
                        blockSize = TransferManager.Configurations.BlockSize;
                        memoryChunksRequiredEachTime = (int)Math.Ceiling((double)blockSize / Constants.DefaultBlockSize);
                    }
                    else
                    {
                        // Try to increase the memory pool size
                        this.Scheduler.TransferOptions.UpdateMaximumCacheSize(blockSize);
                    }

                    // If data size is smaller than block size, fit block size according to total length, in order to minimize buffer allocation,
                    // and save space and time.
                    if (this.SharedTransferData.TotalLength < blockSize)
                    {
                        // Note total length could be 0, in this case, use default block size.
                        memoryChunksRequiredEachTime = Math.Max(1,
                                                                (int)Math.Ceiling((double)this.SharedTransferData.TotalLength / Constants.DefaultBlockSize));
                        blockSize = memoryChunksRequiredEachTime * Constants.DefaultBlockSize;
                    }
                    this.SharedTransferData.BlockSize = blockSize;
                    this.SharedTransferData.MemoryChunksRequiredEachTime = memoryChunksRequiredEachTime;
                }
                else
                {
                    // For normal directions, we'll use default block size 4MB for transfer.
                    this.SharedTransferData.BlockSize = Constants.DefaultBlockSize;
                    this.SharedTransferData.MemoryChunksRequiredEachTime = 1;
                }
            };
        }