Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions MD5/MD5.sln
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.4.33403.182
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MD5", "MD5\MD5.csproj", "{A1FB78F7-7A8A-4EC2-AB83-C049DF18BB6C}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestsMD5", "TestsMD5\TestsMD5.csproj", "{330AFC94-ABE1-4802-8712-134A7848F562}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{A1FB78F7-7A8A-4EC2-AB83-C049DF18BB6C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A1FB78F7-7A8A-4EC2-AB83-C049DF18BB6C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A1FB78F7-7A8A-4EC2-AB83-C049DF18BB6C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A1FB78F7-7A8A-4EC2-AB83-C049DF18BB6C}.Release|Any CPU.Build.0 = Release|Any CPU
{330AFC94-ABE1-4802-8712-134A7848F562}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{330AFC94-ABE1-4802-8712-134A7848F562}.Debug|Any CPU.Build.0 = Debug|Any CPU
{330AFC94-ABE1-4802-8712-134A7848F562}.Release|Any CPU.ActiveCfg = Release|Any CPU
{330AFC94-ABE1-4802-8712-134A7848F562}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {3D47FDE2-DD6A-4102-8AE7-947763D5FA8D}
EndGlobalSection
EndGlobal
10 changes: 10 additions & 0 deletions MD5/MD5/MD5.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

</Project>
90 changes: 90 additions & 0 deletions MD5/MD5/MultiThreadMD5.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
using System.Text;
using System.Security.Cryptography;

namespace Md5;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Лучше неймспейс в самом начале файла (после шапки с лицензией)


public class MultiThreadMD5

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • Самому классу тоже нужен комментарий
  • Все методы static, значит и сам класс по логике вещей должен быть static

{
/// <summary>
/// Calculating file using threads
/// </summary>
/// <param name="path">path where file</param>
/// <returns>md5 hash</returns>
public static async Task<byte[]> CalculateFile(string path)
{
if (!File.Exists(path))
{
throw new FileNotFoundException();
}
var fstream = File.OpenRead(path);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
var fstream = File.OpenRead(path);
using var fstream = File.OpenRead(path);

using var md5 = MD5.Create();
byte[] buffer = new byte[fstream.Length];

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
byte[] buffer = new byte[fstream.Length];
var buffer = new byte[fstream.Length];

await fstream.ReadAsync(buffer, 0, buffer.Length);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Зачитывать весь файл в массив может быть хорошей идеей, если мы знаем, что у нас ОЧЕНЬ маленькие файлы и ОЧЕНЬ много оперативки. Поэтому у ComputeHash есть перегрузка, принимающая Stream (она сама вычитывает файл порциями и считает хеш поблочно).

return md5.ComputeHash(buffer);
}

private static async Task TakeFilesAndDirectoriesFromSubDirectories(string path, List<string> listDirectoriesAndFiles)
{
var allDirectories = Directory.GetDirectories(path);
foreach (string directory in allDirectories)
{
listDirectoriesAndFiles.Add(directory);
var allFiles = Directory.GetFiles(directory);

foreach (string file in allFiles)
{
listDirectoriesAndFiles.Add(file);
}
await TakeFilesAndDirectoriesFromSubDirectories(directory, listDirectoriesAndFiles);
}
}

private static async Task TakeFilesAndDirectoriesFromDirectory(string path, List<string> listDirectoriesAndFiles)
{
listDirectoriesAndFiles.Add(path);
foreach (string file in Directory.GetFiles(path))
{
listDirectoriesAndFiles.Add(file);
}
await TakeFilesAndDirectoriesFromSubDirectories(path, listDirectoriesAndFiles);
}

/// <summary>
/// Calculating md5 hash using threads
/// </summary>
public static async Task<byte[]> CalculateDirectory(string path)
{
if (!Directory.Exists(path))
{
throw new DirectoryNotFoundException();
}

var listAllDirectoriesAndFliles = new List<string>();

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
var listAllDirectoriesAndFliles = new List<string>();
var listAllDirectoriesAndFiles = new List<string>();

await TakeFilesAndDirectoriesFromDirectory(path, listAllDirectoriesAndFliles);
var arrayList = listAllDirectoriesAndFliles.ToArray();
var arrayWithNumber = new byte[arrayList.Length][];
Parallel.For(0, arrayList.Length, i =>
{
using var md5 = MD5.Create();
if (File.Exists(arrayList[i]))
{
arrayWithNumber[i] = CalculateFile(arrayList[i]).Result;
}
else
{
arrayWithNumber[i] = md5.ComputeHash(Encoding.UTF8.GetBytes(arrayList[i]));
}
});
Comment on lines +66 to +77

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Это не тот же самый алгоритм вычисления хеша, что просили в условии. Там хеш считается рекурсивно, тут просто по порядку хешируется всё содержимое директорий.


var listByte = new List<byte>();

foreach (var element in arrayWithNumber)
{
foreach (var _byte in element)
{
listByte.Add(_byte);
}
}
Comment on lines +81 to +87

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Результатом по условию должен быть MD5-хеш, который 16-байтовый массив. А тут массив будет такого размера, какого получится. Ответ неправильный :)

return listByte.ToArray();
}
}
88 changes: 88 additions & 0 deletions MD5/MD5/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
using Md5;
using System.Diagnostics;

public class Program
{
public static async Task<int> Main()
{
Comment on lines +4 to +7

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Не используйте Main, используйте top-level operators.

Console.WriteLine("Далее будет выполнено сравнение Md5 hash с использованием многопоточности и без неё");
Console.WriteLine("Введите путь до дирректории");
var pathToDirrectory = Console.ReadLine();

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
var pathToDirrectory = Console.ReadLine();
var pathToDirectory = Console.ReadLine();

if (pathToDirrectory == null )
{
throw new NullReferenceException();

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Никогда не кидайте NullReferenceException, оно всегда должно указывать на ошибку в коде (и кидаться .NET-машиной).

}
Stopwatch stopwatchForDirrectory = new Stopwatch();

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Stopwatch stopwatchForDirrectory = new Stopwatch();
Stopwatch stopwatchForDirectory = new();

stopwatchForDirrectory.Start();
try
{
var hash = SingleMd5.CalculateDirectory(pathToDirrectory);
}
catch (DirectoryNotFoundException)
{
Console.WriteLine("Проблемы с путём к дирректории");
stopwatchForDirrectory.Stop();
return 0;
}
stopwatchForDirrectory.Stop();
Console.WriteLine();
Console.Write("Время исполнения подсчёта hash с использованием 1 потока: ");
Console.WriteLine(stopwatchForDirrectory.ElapsedMilliseconds);
stopwatchForDirrectory.Reset();
stopwatchForDirrectory.Start();
try
{
var hash = await MultiThreadMD5.CalculateDirectory(pathToDirrectory);
}
catch (DirectoryNotFoundException)
{
Console.WriteLine("Проблемы с путём к дирректории");
stopwatchForDirrectory.Stop();
return 0;
}
stopwatchForDirrectory.Stop();
Console.Write("Время исполнения подсчёта hash с использованием нескольких потоков: ");
Console.WriteLine(stopwatchForDirrectory.ElapsedMilliseconds);

Console.WriteLine();
Console.WriteLine("Ввдеите путь до файла");
var pathToFile = Console.ReadLine();
if (pathToFile == null)
{
throw new NullReferenceException();
}
Stopwatch stopwatchForFile = new Stopwatch();
stopwatchForFile.Start();
try
{
var hash = SingleMd5.CalculateFile(pathToFile);
}
catch (FileNotFoundException)
{
Console.WriteLine("Проблемы с путём к файлу");
stopwatchForDirrectory.Stop();
return 0;
}
stopwatchForFile.Stop();
Console.WriteLine();
Console.Write("Время исполнения подсчёта hash с использованием 1 потока: ");
Console.WriteLine(stopwatchForFile.ElapsedMilliseconds);
stopwatchForFile.Reset();
stopwatchForFile.Start();
try
{
var hash = await MultiThreadMD5.CalculateFile(pathToFile);
}
catch (FileNotFoundException)
{
Console.WriteLine("Проблемы с путём к файлу");
stopwatchForDirrectory.Stop();
return 0;
}
stopwatchForFile.Stop();
Console.Write("Время исполнения подсчёта hash с использованием нескольких потоков: ");
Console.WriteLine(stopwatchForFile.ElapsedMilliseconds);

return 0;
}
}
77 changes: 77 additions & 0 deletions MD5/MD5/SingleThreadMD5.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
using System.Text;
using System.Security.Cryptography;

namespace Md5;

public class SingleMd5
{
/// <summary>
/// Calculating file by path
/// </summary>
/// <param name="path">Where file is lies</param>
/// <returns>md5 hash</returns>
public static byte[] CalculateFile(string path)
{
if (!File.Exists(path))
{
throw new FileNotFoundException();
}
var fstream = File.OpenRead(path);
using var md5 = MD5.Create();
return md5.ComputeHash(fstream);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Кажется, это можно было бы переиспользовать и в многопоточном варианте.

}

private static void TakeFilesAndDirectoriesFromSubDirectories(string path, List<string> listDirectoriesAndFiles)
{
var allDirectories = Directory.GetDirectories(path);
foreach (string directory in allDirectories)
{
listDirectoriesAndFiles.Add(directory);
var allFiles = Directory.GetFiles(directory);

foreach (string file in allFiles)
{
listDirectoriesAndFiles.Add(file);
}
TakeFilesAndDirectoriesFromSubDirectories(directory, listDirectoriesAndFiles);
}
}

private static void TakeFilesAndDirectoriesFromDirectory(string path, List<string> listDirectoriesAndFiles)
{
listDirectoriesAndFiles.Add(path);
foreach (string file in Directory.GetFiles(path))
{
listDirectoriesAndFiles.Add(file);
}
TakeFilesAndDirectoriesFromSubDirectories(path, listDirectoriesAndFiles);
}

/// <summary>
/// Calculating md5 hash using one thread
/// </summary>
public static byte[] CalculateDirectory(string path)
{
if (!Directory.Exists(path))
{
throw new DirectoryNotFoundException();
}

var listAllDirectoriesAndFliles = new List<string>();
TakeFilesAndDirectoriesFromDirectory(path, listAllDirectoriesAndFliles);
using var md5 = MD5.Create();
var result = new byte[0];
foreach (var fileOrDirectory in listAllDirectoriesAndFliles)
{
if (File.Exists(fileOrDirectory))
{
result = result.Concat(CalculateFile(fileOrDirectory)).ToArray();
}
else
{
result = result.Concat(md5.ComputeHash(Encoding.UTF8.GetBytes(fileOrDirectory))).ToArray();
}
}
return result;
}
}
51 changes: 51 additions & 0 deletions MD5/TestsMD5/TestsMD5.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
namespace TestsMD5;

using Md5;

public class Tests
{
private string pathDirectory = Path.Combine(TestContext.CurrentContext.TestDirectory, "ForTests");
private string pathFile = Path.Combine(TestContext.CurrentContext.TestDirectory, "ForTests", "file1.txt");

[Test]
public async Task AreTheResultsSameWithDirectories()
{
var hashMultiThreads = await MultiThreadMD5.CalculateDirectory(pathDirectory);
var hashSingleThread = SingleMd5.CalculateDirectory(pathDirectory);

Assert.That(hashMultiThreads, Is.EquivalentTo(hashSingleThread));
}

[Test]
public async Task AreTheResultsSameWithFiles()
{
var hashMultiThreads = await MultiThreadMD5.CalculateFile(pathFile);
var hashSingleThread = SingleMd5.CalculateFile(pathFile);

Assert.That(hashMultiThreads, Is.EquivalentTo(hashSingleThread));
}

[Test]
public void IsThrowExceptionWhenIncorrectPathDirrectoryInSingleThread()
{
Assert.Throws<DirectoryNotFoundException>(() => SingleMd5.CalculateDirectory(pathFile));
}

[Test]
public void IsThrowExceptionWhenIncorrectPathDirrectoryInMultiThread()
{
Assert.ThrowsAsync<DirectoryNotFoundException>(() => MultiThreadMD5.CalculateDirectory(pathFile));
}

[Test]
public void IsThrowExceptionWhenIncorrectPathFileInSingleThread()
{
Assert.Throws<FileNotFoundException>(() => SingleMd5.CalculateFile(pathDirectory + "/ttrt.txt"));
}

[Test]
public void IsThrowExceptionWhenIncorrectPathFileInMultiThread()
{
Assert.ThrowsAsync<FileNotFoundException>(() => MultiThreadMD5.CalculateFile(pathDirectory + "/ttrt.txt"));
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Можно было бы использовать TestCaseSource, чтобы избежать копипаста.

}
23 changes: 23 additions & 0 deletions MD5/TestsMD5/TestsMD5.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>

<IsPackable>false</IsPackable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.2" />
<PackageReference Include="NUnit" Version="3.13.3" />
<PackageReference Include="NUnit3TestAdapter" Version="4.2.1" />
<PackageReference Include="NUnit.Analyzers" Version="3.3.0" />
<PackageReference Include="coverlet.collector" Version="3.1.2" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\MD5\MD5.csproj" />
</ItemGroup>

</Project>
1 change: 1 addition & 0 deletions MD5/TestsMD5/Usings.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
global using NUnit.Framework;
1 change: 1 addition & 0 deletions MD5/TestsMD5/bin/Debug/net7.0/ForTests/file1.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
1
1 change: 1 addition & 0 deletions MD5/TestsMD5/bin/Debug/net7.0/ForTests/file2.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
2
1 change: 1 addition & 0 deletions MD5/TestsMD5/bin/Debug/net7.0/ForTests/folder2/file3.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
3