Beispiel #1
0
        private static async Task RunAsync(IReadOnlyList <string> Arguments)
        {
            // Get given version of the async producer / consumer pipeline.
            var version  = int.Parse(Arguments[0]);
            var pipeline = Pipeline.Create(version);
            // Get proxy to math service and configure initial values.
            var httpClient = new HttpClient(new CacheBustingMessageHandler())
            {
                BaseAddress = new Uri("http://localhost:65447")
            };
            var mathService = RestService.For <IMathService>(httpClient);
            var inputValues = new long[_inputValueCount];

            for (var index = 0; index < _inputValueCount; index++)
            {
                inputValues[index] = index + 1;
            }
            var stepValues = new long[] { 4, 99, 13, 41 };
            // Run pipeline.
            var stopwatch = Stopwatch.StartNew();
            await pipeline.Run(mathService, inputValues, stepValues);

            stopwatch.Stop();
            ThreadsafeConsole.WriteLine($"Pipeline ran in {stopwatch.Elapsed.TotalSeconds:0.000} seconds.");
        }
Beispiel #2
0
        // ReSharper disable once ParameterTypeCanBeEnumerable.Global
        protected static void DisplayValues(long[] InputValues, long[] StepValues, long[][] OutputValues)
        {
            var stringBuilder = new StringBuilder();

            stringBuilder.Append("Step Values: ");
            foreach (var stepValue in StepValues)
            {
                stringBuilder.Append($"{stepValue,2} ");
            }
            stringBuilder.AppendLine();
            stringBuilder.AppendLine();
            stringBuilder.AppendLine("InputValue      Step 1 (Power)      Step 2 (Add)  Step 3 (Multiply)    Step 4 (Modulo)");
            for (var index = 0; index < OutputValues.Length; index++)
            {
                var inputValue = InputValues[index];
                stringBuilder.Append($"{inputValue,10}");
                var stepValues = OutputValues[index];
                foreach (var stepValue in stepValues)
                {
                    stringBuilder.Append($"{stepValue,19:N0}");
                }
                stringBuilder.AppendLine();
            }
            ThreadsafeConsole.WriteLine(stringBuilder.ToString());
        }
Beispiel #3
0
        private static void TestFileSerialization()
        {
            BaseballRepo.UseFile = true;
            IBaseballRepo repo         = new BaseballRepo();
            var           cubs1984Team = repo.CreateTeam();

            PopulateCubs1984Team(repo, cubs1984Team);
            PrintSalaries(cubs1984Team);
            ThreadsafeConsole.WriteLine();
            ThreadsafeConsole.WriteLine();
            cubs1984Team.Save();
            ThreadsafeConsole.WriteLine("Saving team to JSON text file.");
            ThreadsafeConsole.WriteLine("Loading team from JSON text file.");
            ThreadsafeConsole.WriteLine();
            ThreadsafeConsole.WriteLine();
            cubs1984Team = repo.GetTeam(1);
            PrintSalaries(cubs1984Team);
            ThreadsafeConsole.WriteLine();
            ThreadsafeConsole.WriteLine();
            ThreadsafeConsole.WriteLine("Saving team to JSON text file.");
            LeagueRegulations.TeamSalaryCap = 40_000_000m;
            ThreadsafeConsole.WriteLine($"Changing team salary cap to {LeagueRegulations.TeamSalaryCap:C0}.");
            ThreadsafeConsole.WriteLine("Loading team from JSON text file.");
            ThreadsafeConsole.WriteLine();
            ThreadsafeConsole.WriteLine();
            cubs1984Team = repo.GetTeam(1);
            PrintSalaries(cubs1984Team);
        }
Beispiel #4
0
        private static async Task Run(IReadOnlyList <string> Arguments)
        {
            var stopwatch = Stopwatch.StartNew();

            var(phoneNumberCount, sortPhoneNumberFile) = ParseCommandLine(Arguments);
            // Create input file of phone numbers.
            var inputFilename  = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) ?? string.Empty, "InputPhoneNumbers.txt");
            var outputFilename = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) ?? string.Empty, "OutputPhoneNumbers.txt");

            ThreadsafeConsole.WriteLine("Creating input file of phone numbers.", ConsoleColor.White, stopwatch);
            await CreateInputFile(inputFilename, phoneNumberCount);

            ThreadsafeConsole.WriteLine("Done.", ConsoleColor.White, stopwatch);
            // Create sorted output file of phone numbers.
            ThreadsafeConsole.WriteLine("Creating output file of phone numbers.", ConsoleColor.White, stopwatch);
            var sortStart = stopwatch.Elapsed;

            await sortPhoneNumberFile(inputFilename, outputFilename);

            var sortEnd      = stopwatch.Elapsed;
            var sortDuration = sortEnd - sortStart;

            ThreadsafeConsole.WriteLine("Done.", ConsoleColor.White, stopwatch);
            ThreadsafeConsole.WriteLine($"Sort took {sortDuration.TotalSeconds.ToString(_elapsedSecondsFormat)} seconds.");
        }
Beispiel #5
0
        private static void TestSqlSerialization()
        {
            BaseballRepo.UseFile            = false;
            LeagueRegulations.TeamSalaryCap = 40_000_000m;
            ThreadsafeConsole.WriteLine($"Changing team salary cap to {LeagueRegulations.TeamSalaryCap:C0}.");
            ThreadsafeConsole.WriteLine("Loading team from database.");
            IBaseballRepo repo         = new BaseballRepo();
            var           cubs1984Team = repo.GetTeam(1);

            PrintSalaries(cubs1984Team);
        }
Beispiel #6
0
 public static void Main(string[] Arguments)
 {
     try
     {
         Run(Arguments);
     }
     catch (Exception exception)
     {
         ThreadsafeConsole.WriteLine(exception.GetSummary(true, true), ConsoleColor.Red);
     }
 }
Beispiel #7
0
        private static async Task DecryptAsync(string InputPath)
        {
            // Get password from user.
            // TODO: Hide and confirm password.
            ThreadsafeConsole.Write("Enter password: "******"Output filename is {outputFilename}.");
                // Generate key from password (provided by user) and salt (stored in encrypted file).
                using (var keyDerivation = KeyDerivation.Create(encryptedFileHeader.KeyDerivationAlgorithm, password, encryptedFileHeader.Salt, encryptedFileHeader.KeyDerivationIterations))
                {
                    var key = keyDerivation.GetBytes(encryptedFileHeader.KeyLength);
                    ThreadsafeConsole.WriteLine($"Encryption key (derived from password and salt) is {Convert.ToBase64String(key)}.");
                    ThreadsafeConsole.WriteLine($"Cipher initialization vector is {Convert.ToBase64String(encryptedFileHeader.InitializationVector)}.");
                    // Create cipher from key (see above) plus algorithm name and initialization vector (stored in unencrypted header at beginning of encrypted file).
                    // Create decrypting input stream.
                    using (var cipher = Cipher.Create(encryptedFileHeader.CipherAlgorithm))
                        using (var decryptor = cipher.CreateDecryptor(key, encryptedFileHeader.InitializationVector))
                            await using (var cryptoStream = new CryptoStream(inputFileStream, decryptor, CryptoStreamMode.Read))
                                await using (var outputFileStream = File.Open(outputFilename, FileMode.CreateNew, FileAccess.Write, FileShare.None))
                                {
                                    // To limit memory usage, repeatedly read a small block from input stream and write it to the decrypted output stream.
                                    var buffer = new byte[cipher.BlockSize];
                                    int bytesRead;
                                    while ((bytesRead = await cryptoStream.ReadAsync(buffer, 0, buffer.Length)) > 0)
                                    {
                                        await outputFileStream.WriteAsync(buffer, 0, bytesRead);
                                    }
                                }
                }
            }
            var encryptionDuration = _stopwatch.Elapsed - encryptionStart;

            ThreadsafeConsole.WriteLine($"Wrote decrypted file to {outputFilename}.");
            ThreadsafeConsole.WriteLine($"Decryption took {encryptionDuration.TotalSeconds.ToString(_elapsedSecondsFormat)} seconds.");
        }
Beispiel #8
0
        public static async Task Main(string[] Arguments)
        {
            try
            {
                await Run(Arguments);

                ThreadsafeConsole.WriteLine(null);
            }
            catch (Exception exception)
            {
                ThreadsafeConsole.WriteLine(exception.GetSummary(true, true), ConsoleColor.Red);
            }
        }
Beispiel #9
0
        // ReSharper disable once SuggestBaseTypeForParameter
        private static async Task TestReadPerformance(Client NaClient, Region AsRegion)
        {
            // Test client performance.
            Console.WriteLine("Testing performance of North American client.");
            Console.WriteLine();
            Console.Write("Writing sentinel value... ");
            TimeSpan begin = _stopwatch.Elapsed;
            await NaClient.WriteValueAsync(_sentinelKey, "Yada, yada, yada");

            TimeSpan end = _stopwatch.Elapsed;
            TimeSpan clientWriteDuration = end - begin;

            Console.WriteLine("done.");
            Console.WriteLine($"Wrote sentinel value in {clientWriteDuration.TotalSeconds.ToString(_timeSpanFormat)} seconds.");
            Console.WriteLine();
            Console.Write("Reading sentinel value... ");
            begin = _stopwatch.Elapsed;
            string sentinelValue = await NaClient.ReadValueAsync(_sentinelKey);

            end = _stopwatch.Elapsed;
            TimeSpan clientReadDuration = end - begin;

            Console.WriteLine("done.");
            Console.WriteLine($"Sentinel value = {sentinelValue}.");
            Console.WriteLine($"Read sentinel value in {clientReadDuration.TotalSeconds.ToString(_timeSpanFormat)} seconds.");
            Console.WriteLine();
            // Compare client read performance to reading from a high-latency connection.
            Console.WriteLine("Testing performance of connection between Asian node and North American node.");
            Console.Write("Reading sentinel value... ");
            begin = _stopwatch.Elapsed;
            Connection asToNaConnection = AsRegion.Nodes[0].Connections[RegionName.NorthAmerica][0];

            sentinelValue = await asToNaConnection.GetValueAsync(_sentinelKey);

            end = _stopwatch.Elapsed;
            TimeSpan highLatencyDuration = end - begin;

            Console.WriteLine("done.");
            Console.WriteLine($"Sentinel value = {sentinelValue}.");
            Console.WriteLine($"Read sentinel value in {highLatencyDuration.TotalSeconds.ToString(_timeSpanFormat)} seconds.");
            Console.WriteLine();
            Console.Write("Test result: ");
            if (clientReadDuration < highLatencyDuration)
            {
                ThreadsafeConsole.WriteLine("success.", ConsoleColor.Green);
            }
            else
            {
                ThreadsafeConsole.WriteLine("failure.", ConsoleColor.Red);
            }
        }
Beispiel #10
0
 public static async Task Main(string[] Arguments)
 {
     try
     {
         Console.WriteLine();
         if (Arguments?.Length != 1)
         {
             throw new ArgumentException("Test name not specified.");
         }
         await Run(Arguments);
     }
     catch (Exception exception)
     {
         ThreadsafeConsole.WriteLine(exception.GetSummary(true, true), ConsoleColor.Red);
     }
     finally
     {
         Console.WriteLine();
     }
 }
Beispiel #11
0
 public static void Main(string[] Arguments)
 {
     try
     {
         Console.WriteLine();
         if ((Arguments == null) || (Arguments.Length == 0))
         {
             throw new ArgumentException($"{nameof(Arguments)} must specify an integer version.", nameof(Arguments));
         }
         Run(Arguments);
     }
     catch (Exception exception)
     {
         ThreadsafeConsole.WriteLine(exception.GetSummary(true, true), ConsoleColor.Red);
     }
     finally
     {
         Console.WriteLine();
     }
 }
Beispiel #12
0
        private static void PrintSalaries(ITeam Team)
        {
            // Sort by salary descending.
            var teamMembers = Team.GetAllTeamMembers().ToList();

            ThreadsafeConsole.WriteLine($"Team Salary Cap = {LeagueRegulations.TeamSalaryCap:C0}.");
            ThreadsafeConsole.WriteLine();
            teamMembers.Sort((TeamMember1, TeamMember2) => TeamMember2.Salary.CompareTo(TeamMember1.Salary));
            const string columnSpacer = "     ";

            ThreadsafeConsole.WriteLine($"{"Team Member", -_namePadding}{columnSpacer}{"Salary", _salaryPadding}");
            ThreadsafeConsole.WriteLine(new string('=', _namePadding + _salaryPadding + columnSpacer.Length));
            foreach (var teamMember in teamMembers)
            {
                ThreadsafeConsole.WriteLine($"{teamMember.Name, -_namePadding}{teamMember.Salary, _namePadding:C0}");
            }
            var totalSalaries = teamMembers.Sum(TeamMember => TeamMember.Salary);

            ThreadsafeConsole.WriteLine(new string('=', _namePadding + _salaryPadding + columnSpacer.Length));
            ThreadsafeConsole.WriteLine($"{"Total", -_namePadding}{totalSalaries, _namePadding:C0}");
        }
Beispiel #13
0
        private static void TestSalaryUpdate_23_7()
        {
            IBaseballRepo repo         = new BaseballRepo();
            var           cubs1984Team = repo.CreateTeam();

            PopulateCubs1984Team(repo, cubs1984Team);
            PrintSalaries(cubs1984Team);
            ThreadsafeConsole.WriteLine();
            ThreadsafeConsole.WriteLine();
            const decimal sandbergIncrease = 7_000_000m;
            const decimal davisIncrease    = 2_000_000m;

            // ReSharper disable PossibleNullReferenceException
            cubs1984Team.Players.First(Player => Player.JerseyNumber == 23).Salary += sandbergIncrease;
            cubs1984Team.Players.First(Player => Player.JerseyNumber == 07).Salary += davisIncrease;
            ThreadsafeConsole.WriteLine($"Increase Sandberg's salary by {sandbergIncrease:C0}");
            ThreadsafeConsole.WriteLine($"Increase Davis'     salary by {davisIncrease:C0}");
            ThreadsafeConsole.WriteLine();
            ThreadsafeConsole.WriteLine();
            // ReSharper restore PossibleNullReferenceException
            PrintSalaries(cubs1984Team);
        }
Beispiel #14
0
        private static async Task RunAsync(IReadOnlyList <string> Arguments)
        {
            _stopwatch = Stopwatch.StartNew();
            var encryptedFileHeader = ParseCommandLine(Arguments);

            ThreadsafeConsole.WriteLine();
            // ReSharper disable once SwitchStatementHandlesSomeKnownEnumValuesWithDefault
            switch (encryptedFileHeader.Operation)
            {
            case Operation.Encrypt:
                await EncryptAsync(encryptedFileHeader);

                break;

            case Operation.Decrypt:
                await DecryptAsync(encryptedFileHeader.Filename);

                break;

            default:
                throw new ArgumentException($"{encryptedFileHeader.Operation} operation not supported.");
            }
        }
Beispiel #15
0
        // ReSharper disable once SuggestBaseTypeForParameter
        private static async Task TestEventualConsistency(Client NaClient, Region NaRegion, List <NodeBase> GlobalNodes)
        {
            Console.WriteLine("Testing eventual consistency of global nodes.");
            Console.WriteLine();
            Console.Write("Writing sentinel value... ");
            TimeSpan begin = _stopwatch.Elapsed;
            await NaClient.WriteValueAsync(_sentinelKey, "Before");

            TimeSpan end = _stopwatch.Elapsed;
            TimeSpan clientWriteDuration = end - begin;

            Console.WriteLine("done.");
            Console.WriteLine($"Wrote sentinel value in {clientWriteDuration.TotalSeconds.ToString(_timeSpanFormat)} seconds.");
            Console.WriteLine();
            // Wait for writes to complete globally.
            Console.Write("Waiting for write to complete globally... ");
            await Task.Delay(TimeSpan.FromSeconds(1));

            Console.WriteLine("done.");
            Console.WriteLine();
            Console.Write("Reading sentinel value... ");
            begin = _stopwatch.Elapsed;
            string sentinelValue = await NaClient.ReadValueAsync(_sentinelKey);

            end = _stopwatch.Elapsed;
            TimeSpan clientReadDuration = end - begin;

            Console.WriteLine("done.");
            Console.WriteLine($"Sentinel value = {sentinelValue}.");
            Console.WriteLine($"Read sentinel value in {clientReadDuration.TotalSeconds.ToString(_timeSpanFormat)} seconds.");
            Console.WriteLine();
            // Take two regional nodes offline.
            Console.Write("Taking two (of five) nodes offline... ");
            NaRegion.Nodes[2].Online = false;
            NaRegion.Nodes[3].Online = false;
            Console.WriteLine("done.");
            Console.WriteLine();
            Console.Write("Updating sentinel value... ");
            begin = _stopwatch.Elapsed;
            await NaClient.WriteValueAsync(_sentinelKey, "After");

            end = _stopwatch.Elapsed;
            clientWriteDuration = end - begin;
            Console.WriteLine("done.");
            Console.WriteLine($"Wrote sentinel value in {clientWriteDuration.TotalSeconds.ToString(_timeSpanFormat)} seconds.");
            Console.WriteLine();
            // Wait for writes to complete globally.
            Console.Write("Waiting for write to complete globally... ");
            await Task.Delay(TimeSpan.FromSeconds(1));

            Console.WriteLine("done.");
            Console.WriteLine();
            // Examine value of sentinel key in global nodes while two regional nodes are offline.
            foreach (NodeBase node in GlobalNodes)
            {
                Console.WriteLine($"{node.Name} node's {_sentinelKey} = {node.GetValue(_sentinelKey)}.");
            }
            Console.WriteLine();
            // Bring two offline nodes back online.
            NaRegion.Nodes[2].Online = true;
            NaRegion.Nodes[3].Online = true;
            Console.Write("Triggering read repairs by reading sentinel value again... ");
            begin         = _stopwatch.Elapsed;
            sentinelValue = await NaClient.ReadValueAsync(_sentinelKey);

            end = _stopwatch.Elapsed;
            clientReadDuration = end - begin;
            Console.WriteLine("done.");
            Console.WriteLine("Read is done.  However, read repairs are in progress.");
            Console.WriteLine($"Sentinel value = {sentinelValue}.");
            Console.WriteLine($"Read sentinel value in {clientReadDuration.TotalSeconds.ToString(_timeSpanFormat)} seconds.");
            Console.WriteLine();
            // Examine value of sentinel key in global nodes.  All nodes are back online and read repairs are in progress.
            HashSet <string> nodeValues = new HashSet <string>();

            foreach (NodeBase node in GlobalNodes)
            {
                string value = node.GetValue(_sentinelKey);
                if (!nodeValues.Contains(value))
                {
                    nodeValues.Add(value);
                }
                Console.WriteLine($"{node.Name} node's {_sentinelKey} = {value}.");
            }
            Console.WriteLine();
            bool consistentDuringReadRepairs = nodeValues.Count == 1;

            // Wait for read repairs to complete.
            Console.Write("Waiting for read repairs to complete... ");
            await Task.Delay(TimeSpan.FromSeconds(1));

            Console.WriteLine("done.");
            Console.WriteLine();
            // Examine value of sentinel key in global nodes.  All nodes are back online and read repairs are complete.
            nodeValues.Clear();
            foreach (NodeBase node in GlobalNodes)
            {
                string value = node.GetValue(_sentinelKey);
                if (!nodeValues.Contains(value))
                {
                    nodeValues.Add(value);
                }
                Console.WriteLine($"{node.Name} node's {_sentinelKey} = {value}.");
            }
            Console.WriteLine();
            bool consistentAfterReadRepairs = nodeValues.Count == 1;
            bool success = !consistentDuringReadRepairs && consistentAfterReadRepairs;

            Console.Write("Test result: ");
            if (success)
            {
                ThreadsafeConsole.WriteLine("success.", ConsoleColor.Green);
            }
            else
            {
                ThreadsafeConsole.WriteLine("failure.", ConsoleColor.Red);
            }
        }
Beispiel #16
0
        // ReSharper disable once SuggestBaseTypeForParameter
        private static async Task TestFaultTolerance(Client NaClient, Region NaRegion)
        {
            Console.WriteLine("Testing fault tolerance of North American nodes.");
            Console.WriteLine();
            Console.Write("Writing sentinel value... ");
            TimeSpan begin = _stopwatch.Elapsed;
            await NaClient.WriteValueAsync(_sentinelKey, "Yada, yada, yada");

            TimeSpan end = _stopwatch.Elapsed;
            TimeSpan clientWriteDuration = end - begin;

            Console.WriteLine("done.");
            Console.WriteLine($"Wrote sentinel value in {clientWriteDuration.TotalSeconds.ToString(_timeSpanFormat)} seconds.");
            Console.WriteLine();
            Console.Write("Reading sentinel value... ");
            begin = _stopwatch.Elapsed;
            string sentinelValue = await NaClient.ReadValueAsync(_sentinelKey);

            end = _stopwatch.Elapsed;
            TimeSpan clientReadDuration = end - begin;

            Console.WriteLine("done.");
            Console.WriteLine($"Sentinel value = {sentinelValue}.");
            Console.WriteLine($"Read sentinel value in {clientReadDuration.TotalSeconds.ToString(_timeSpanFormat)} seconds.");
            Console.WriteLine();
            // Take two nodes offline.  Expect no service interruption.
            Console.Write("Taking two (of five) nodes offline... ");
            NaRegion.Nodes[2].Online = false;
            NaRegion.Nodes[3].Online = false;
            Console.WriteLine("done.");
            Console.Write("Reading sentinel value... ");
            begin         = _stopwatch.Elapsed;
            sentinelValue = await NaClient.ReadValueAsync(_sentinelKey);

            end = _stopwatch.Elapsed;
            clientReadDuration = end - begin;
            Console.WriteLine("done.");
            Console.WriteLine($"Sentinel value = {sentinelValue}.");
            Console.WriteLine($"Read sentinel value in {clientReadDuration.TotalSeconds.ToString(_timeSpanFormat)} seconds.");
            Console.WriteLine();
            // Take a third node offline.  Expect service interruption.
            Console.Write("Taking a third (of five) node offline... ");
            NaRegion.Nodes[4].Online = false;
            Console.WriteLine("done.");
            Console.Write("Reading sentinel value... ");
            bool success = false;

            try
            {
                begin         = _stopwatch.Elapsed;
                sentinelValue = await NaClient.ReadValueAsync(_sentinelKey);

                end = _stopwatch.Elapsed;
                clientReadDuration = end - begin;
                Console.WriteLine("done.");
                Console.WriteLine($"Sentinel value = {sentinelValue}.");
                Console.WriteLine($"Read sentinel value in {clientReadDuration.TotalSeconds.ToString(_timeSpanFormat)} seconds.");
                Console.WriteLine();
            }
            catch (QuorumNotReachedException exception)
            {
                // Expect a QuorumNotReachedException because only two of five nodes are online.
                success = true;
                ThreadsafeConsole.WriteLine(exception.GetSummary(true, true), ConsoleColor.Red);
            }
            catch (Exception exception)
            {
                ThreadsafeConsole.WriteLine(exception.GetSummary(true, true), ConsoleColor.Red);
            }
            Console.Write("Test result: ");
            if (success)
            {
                ThreadsafeConsole.WriteLine("success.", ConsoleColor.Green);
            }
            else
            {
                ThreadsafeConsole.WriteLine("failure.", ConsoleColor.Red);
            }
        }
Beispiel #17
0
        // TODO: Add progress bar.
        private static async Task EncryptAsync(EncryptedFileHeader EncryptedFileHeader)
        {
            const string encryptedFileExtension = ".encrypted";
            var          inputPathIsFile        = File.Exists(EncryptedFileHeader.Filename);

            if (!inputPathIsFile && !Directory.Exists(EncryptedFileHeader.Filename))
            {
                throw new Exception($"{EncryptedFileHeader.Filename} input path does not exist.");
            }
            ThreadsafeConsole.WriteLine(inputPathIsFile ? "InputPath is a file." : "InputPath is a directory.");
            // TODO: Support encrypting entire directories using System.IO.Compression.ZipFile class.
            if (!inputPathIsFile)
            {
                throw new NotSupportedException("Encrypting directories is not supported.");
            }
            // Get password from user.
            // TODO: Hide and confirm password.
            ThreadsafeConsole.WriteLine();
            ThreadsafeConsole.Write("Enter password: "******"Output filename is {outputFilename}.");
            var encryptionStart = _stopwatch.Elapsed;

            await using (var inputFileStream = File.Open(EncryptedFileHeader.Filename, FileMode.Open, FileAccess.Read, FileShare.Read))
                await using (var outputFileStream = File.Open(outputFilename, FileMode.CreateNew, FileAccess.Write, FileShare.None))
                {
                    // Generate key from password and random salt.
                    using (var random = new RNGCryptoServiceProvider()) { random.GetBytes(EncryptedFileHeader.Salt); }
                    using (var keyDerivation = KeyDerivation.Create(EncryptedFileHeader.KeyDerivationAlgorithm, password, EncryptedFileHeader.Salt, EncryptedFileHeader.KeyDerivationIterations))
                        using (var cipher = Cipher.Create(EncryptedFileHeader.CipherAlgorithm))
                        {
                            var key = keyDerivation.GetBytes(EncryptedFileHeader.KeyLength);
                            ThreadsafeConsole.WriteLine($"Encryption key (derived from password and a random salt) is {Convert.ToBase64String(key)}.");
                            // Create cipher and generate initialization vector.
                            // Generate a new initialization vector for each encryption to prevent identical plaintexts from producing identical ciphertexts when encrypted using the same key.
                            cipher.GenerateIV();
                            EncryptedFileHeader.InitializationVector = new byte[cipher.IV.Length];
                            cipher.IV.CopyTo(EncryptedFileHeader.InitializationVector, 0);
                            ThreadsafeConsole.WriteLine($"Cipher initialization vector is {Convert.ToBase64String(EncryptedFileHeader.InitializationVector)}.");
                            // Write integer length of encrypted file header followed by the the header bytes.
                            var fileHeaderBytes = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(EncryptedFileHeader));
                            await outputFileStream.WriteAsync(BitConverter.GetBytes(fileHeaderBytes.Length));

                            await outputFileStream.WriteAsync(fileHeaderBytes);

                            // Create encrypting output stream.
                            var buffer = new byte[cipher.BlockSize];
                            using (var encryptor = cipher.CreateEncryptor(key, EncryptedFileHeader.InitializationVector))
                                await using (var cryptoStream = new CryptoStream(outputFileStream, encryptor, CryptoStreamMode.Write))
                                {
                                    // To limit memory usage, repeatedly read a small block from input stream and write it to the encrypted output stream.
                                    int bytesRead;
                                    while ((bytesRead = await inputFileStream.ReadAsync(buffer, 0, buffer.Length)) > 0)
                                    {
                                        await cryptoStream.WriteAsync(buffer, 0, bytesRead);
                                    }
                                }
                        }
                }
            var encryptionDuration = _stopwatch.Elapsed - encryptionStart;

            ThreadsafeConsole.WriteLine($"Wrote encrypted file to {outputFilename}.");
            ThreadsafeConsole.WriteLine($"Encryption took {encryptionDuration.TotalSeconds.ToString(_elapsedSecondsFormat)} seconds.");
        }