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
1 change: 1 addition & 0 deletions Test/Test/MD5Test/Azaza/azaza.txt.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
WFWFWEFWEFWVWwGVWvfegrebke eab fdaeio dferb rtnd dbaeb
1 change: 1 addition & 0 deletions Test/Test/MD5Test/Azaza/uzuzu.txt.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
dfveerbergb serubn er8h ss bevbs wb
29 changes: 29 additions & 0 deletions Test/Test/MD5Test/MD5Test.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
namespace MD5Test;

public class Tests
{
[SetUp]
public void Setup()
{
}
Comment on lines +5 to +8

Choose a reason for hiding this comment

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

Suggested change
[SetUp]
public void Setup()
{
}


[Test]
public async Task ShouldReultRemainTheSameForComputeCheckSumForFile()
{
var firstCompute = await Test.MD5.ComputeCheckSumForFile("..//..//..//text.txt");
var secondCompute = await Test.MD5.ComputeCheckSumForFile("..//..//..//text.txt");
Assert.That(firstCompute, Is.EquivalentTo(secondCompute));
}

[Test]
public void ShouldDirectoryNotFoundException()
{
Assert.ThrowsAsync<DirectoryNotFoundException>(() => Test.MD5.SequentiallyComputeCheckSumForDirectory("..//..//..//Ahaha"));
}

[Test]
public void ShouldFileNotFoundException()
{
Assert.ThrowsAsync<FileNotFoundException>(() => Test.MD5.ComputeCheckSumForFile("Ahaha.txt"));
}
}
23 changes: 23 additions & 0 deletions Test/Test/MD5Test/MD5Test.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<Project Sdk="Microsoft.NET.Sdk">

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

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

<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.1.0" />
<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="..\Test\MD5.csproj" />
</ItemGroup>

</Project>
1 change: 1 addition & 0 deletions Test/Test/MD5Test/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 Test/Test/MD5Test/text.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
wevwevw34g vw834vw 8g3wv hvewrvewrvv ervuuw wev wbvwv sw se ef bef
31 changes: 31 additions & 0 deletions Test/Test/Test.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.2.32505.173
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MD5", "Test\MD5.csproj", "{9CC20C6D-177D-42E0-BB20-9BD0A99E44A1}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MD5Test", "MD5Test\MD5Test.csproj", "{4BAAADE4-FE65-4B66-BE72-FE99B382017A}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{9CC20C6D-177D-42E0-BB20-9BD0A99E44A1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{9CC20C6D-177D-42E0-BB20-9BD0A99E44A1}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9CC20C6D-177D-42E0-BB20-9BD0A99E44A1}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9CC20C6D-177D-42E0-BB20-9BD0A99E44A1}.Release|Any CPU.Build.0 = Release|Any CPU
{4BAAADE4-FE65-4B66-BE72-FE99B382017A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4BAAADE4-FE65-4B66-BE72-FE99B382017A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4BAAADE4-FE65-4B66-BE72-FE99B382017A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4BAAADE4-FE65-4B66-BE72-FE99B382017A}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {C9040189-AFF7-479F-B79D-90D73122038F}
EndGlobalSection
EndGlobal
112 changes: 112 additions & 0 deletions Test/Test/Test/MD5.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
namespace Test;

/// <summary>
/// Class representing the calculation of the check sum
/// </summary>
public class MD5

Choose a reason for hiding this comment

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

Все методы static, следовательно и сам класс мог бы быть static

{
public static async Task<byte[]> ComputeCheckSumForFile(string path)

Choose a reason for hiding this comment

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

А тут комментария не хватает. И я бы его и методы ниже сделал private — не пользователь должен решать, файл у нас или папка. Я как пользователь хочу просто передать путь и получить хеш, думать не хочу.

{
var md5 = System.Security.Cryptography.MD5.Create();
if (!File.Exists(path))
{
throw new FileNotFoundException();
}
using var fileStream = new FileStream(path, FileMode.Open);
var bytes = await md5.ComputeHashAsync(fileStream);
return bytes;
}

/// <summary>
/// Function for sequential calculation of the check sum of the directory
/// </summary>
/// <param name="pathToDirectory">Path to directory</param>
/// <returns></returns>

Choose a reason for hiding this comment

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

Пустые тэги не нужны

/// <exception cref="DirectoryNotFoundException"></exception>
public static async Task<byte[]> SequentiallyComputeCheckSumForDirectory(string pathToDirectory)
{
// Если директории не существует

if (!Directory.Exists(pathToDirectory))
{
throw new DirectoryNotFoundException();
}

// Берем имена файлов
var files = Directory.GetFiles(pathToDirectory);

// Имена директорий
var directories = Directory.GetDirectories(pathToDirectory);
var list = new List<(byte[], string)>();

foreach(var directory in directories)

Choose a reason for hiding this comment

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

Suggested change
foreach(var directory in directories)
foreach (var directory in directories)

{
list.Add((await SequentiallyComputeCheckSumForDirectory(directory), directory));
}

foreach (var file in files)
{
list.Add((await ComputeCheckSumForFile(file), file));
}
var directoryName = System.Text.Encoding.UTF8.GetBytes(Path.GetDirectoryName(pathToDirectory)!);

// Сортируем для одинакового порядка полученных файлов
list.Sort();

foreach (var (lol, bytes) in list)

Choose a reason for hiding this comment

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

lol — League of Legends? :) Потому что в списке идут сначала байты, затем имя

{
var result = new byte[bytes.Length + lol.Length];
directoryName.CopyTo(result, 0);
lol.CopyTo(result, bytes.Length);
directoryName = result;
}

Choose a reason for hiding this comment

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

И я вообще не понял, что тут происходит. Мы на каждой итерации цикла выделяем новый массив, вместо того, чтобы посчитать длину результата и выделить за один раз (избежав квадратичной трудоёмкости по памяти), не пользуемся Array.Concat, почему копирование с bytes.Length, хотя directoryName по длине не равно bytes.Length (и bytes вообще в процессе не участвует, так что зачем его длина, непонятно). И directoryName — это вовсе не directoryName уже после первой итерации.

image

var md5 = System.Security.Cryptography.MD5.Create();
return md5.ComputeHash(directoryName);
}

/// <summary>
/// Function for parallel calculation of the check sum of the directory
/// </summary>
/// <param name="pathToDirectory">Path to directory</param>
/// <returns></returns>
/// <exception cref="DirectoryNotFoundException"></exception>
public static byte[] ParallelComputeCheckSumForDirectory(string pathToDirectory)
{
// Если директории не существует
if (!Directory.Exists(pathToDirectory))
{
throw new DirectoryNotFoundException();
}

// Берем имена файлов
var files = Directory.GetFiles(pathToDirectory);

// Имена директорий
var directories = Directory.GetDirectories(pathToDirectory);

var list = new List<(byte[], string)>();

// Изначально хотел сделать как в домашке с матрицами, а именно дать каждому изпотоков какое то количество файлов на обработку
// После просто Task.Run(() => {...}) и объединять Task.Result
// Но так вроде выглядит проще
Parallel.ForEach(directories, directory => list.Add((ParallelComputeCheckSumForDirectory(directory), directory)));
Parallel.ForEach(files, file => list.Add((ParallelComputeCheckSumForDirectory(file), file)));
Comment on lines +92 to +93

Choose a reason for hiding this comment

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

Вы не верите в гонки? :)

Choose a reason for hiding this comment

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

И ComputeCheckSumForFile тут. Как тестировали, оно же падает сразу? :)


// Сортируем для одинакового порядка полученных файлов
list.Sort();

// Первым идет имя директории
var directoryName = System.Text.Encoding.UTF8.GetBytes(Path.GetDirectoryName(pathToDirectory)!);


foreach (var (lol, bytes) in list)
Comment on lines +99 to +102

Choose a reason for hiding this comment

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

Suggested change
var directoryName = System.Text.Encoding.UTF8.GetBytes(Path.GetDirectoryName(pathToDirectory)!);
foreach (var (lol, bytes) in list)
var directoryName = System.Text.Encoding.UTF8.GetBytes(Path.GetDirectoryName(pathToDirectory)!);
foreach (var (lol, bytes) in list)

{
var result = new byte[bytes.Length + lol.Length];
directoryName.CopyTo(result, 0);
lol.CopyTo(result, bytes.Length);
directoryName = result;
}
var md5 = System.Security.Cryptography.MD5.Create();
return md5.ComputeHash(directoryName);
}
}
10 changes: 10 additions & 0 deletions Test/Test/Test/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>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

</Project>
52 changes: 52 additions & 0 deletions Test/Test/Test/Solution.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
using System.Diagnostics;

if (args.Length < 1)
{
Console.WriteLine("Количество аргументв должно быть не меньше 1");
return;
}


static (IEnumerable<long>, IEnumerable<long>) Calculate(string path)
Comment on lines +7 to +10

Choose a reason for hiding this comment

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

Suggested change
}
static (IEnumerable<long>, IEnumerable<long>) Calculate(string path)
}
static (IEnumerable<long>, IEnumerable<long>) Calculate(string path)

{
var stopWatch = new Stopwatch();
var numberOfIterations = 100;
var standardCalculations = new long[numberOfIterations];
var parallelCalculations = new long[numberOfIterations];
for (int i = 0; i < numberOfIterations; i++)
{
stopWatch.Reset();
stopWatch.Start();
var second = Test.MD5.SequentiallyComputeCheckSumForDirectory(path);
stopWatch.Stop();
standardCalculations[i] = stopWatch.ElapsedMilliseconds;

stopWatch.Reset();
stopWatch.Start();
var first = Test.MD5.ParallelComputeCheckSumForDirectory(path);
stopWatch.Stop();
parallelCalculations[i] = stopWatch.ElapsedMilliseconds;
}

return (standardCalculations, parallelCalculations);
}

foreach (var path in args)
{
var (standardCalculations, parallelCalculations) = Calculate(path);
var averageForStandardCalculations = Enumerable.Average(standardCalculations);
var averageForParallelCalculations = Enumerable.Average(parallelCalculations);
var varianceForStandardCalculations = Enumerable.Average(standardCalculations.Select(x => x * x)) - averageForStandardCalculations * averageForStandardCalculations;
var varianceForParallelCalculations = Enumerable.Average(parallelCalculations.Select(x => x * x)) - averageForParallelCalculations * averageForParallelCalculations;
//stream.WriteLine();
//stream.Write($"{size} {Math.Round(averageForStandardCalculations, 3)} {Math.Round(Math.Sqrt(varianceForStandardCalculations), 3)} {Math.Round(averageForParallelCalculations, 3)} {Math.Round(Math.Sqrt(varianceForParallelCalculations), 3)}");
Comment on lines +41 to +42

Choose a reason for hiding this comment

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

:(

Console.WriteLine($"Average operation time of standart : {Math.Round(averageForStandardCalculations, 3)}");
Console.WriteLine($"Standard deviation operation time of standart: {Math.Round(Math.Sqrt(varianceForStandardCalculations), 3)}");
Console.WriteLine();
Console.WriteLine($"Average operation time of parallel : {Math.Round(averageForParallelCalculations, 3)}");
Console.WriteLine($"Standard deviation operation time of parallel : {Math.Round(Math.Sqrt(varianceForParallelCalculations), 3)}");
Console.WriteLine();
Console.WriteLine();
Console.WriteLine();

}