-
Notifications
You must be signed in to change notification settings - Fork 0
Checksum #5
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Checksum #5
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| | ||
| Microsoft Visual Studio Solution File, Format Version 12.00 | ||
| # Visual Studio Version 17 | ||
| VisualStudioVersion = 17.13.35806.99 d17.13 | ||
| MinimumVisualStudioVersion = 10.0.40219.1 | ||
| Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CheckAmount", "checkAmount\CheckAmount.csproj", "{1AB54304-E937-42B8-929C-533823038651}" | ||
| EndProject | ||
| Global | ||
| GlobalSection(SolutionConfigurationPlatforms) = preSolution | ||
| Debug|Any CPU = Debug|Any CPU | ||
| Release|Any CPU = Release|Any CPU | ||
| EndGlobalSection | ||
| GlobalSection(ProjectConfigurationPlatforms) = postSolution | ||
| {1AB54304-E937-42B8-929C-533823038651}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||
| {1AB54304-E937-42B8-929C-533823038651}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||
| {1AB54304-E937-42B8-929C-533823038651}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||
| {1AB54304-E937-42B8-929C-533823038651}.Release|Any CPU.Build.0 = Release|Any CPU | ||
| EndGlobalSection | ||
| GlobalSection(SolutionProperties) = preSolution | ||
| HideSolutionNode = FALSE | ||
| EndGlobalSection | ||
| GlobalSection(ExtensibilityGlobals) = postSolution | ||
| SolutionGuid = {06282FD8-DD37-410B-853D-14580665948A} | ||
| EndGlobalSection | ||
| EndGlobal |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| <Project Sdk="Microsoft.NET.Sdk"> | ||
|
|
||
| <PropertyGroup> | ||
| <OutputType>Exe</OutputType> | ||
| <TargetFramework>net9.0</TargetFramework> | ||
| <ImplicitUsings>enable</ImplicitUsings> | ||
| <Nullable>enable</Nullable> | ||
| </PropertyGroup> | ||
|
|
||
| <ItemGroup> | ||
| <PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.556"> | ||
| <PrivateAssets>all</PrivateAssets> | ||
| <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> | ||
| </PackageReference> | ||
| </ItemGroup> | ||
|
|
||
| <ItemGroup> | ||
| <AdditionalFiles Include="stylecop.json" /> | ||
| </ItemGroup> | ||
|
|
||
| </Project> |
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,168 @@ | ||||||
| // <copyright file="CheckAmountСalculator.cs" company="Kalinin Andrew"> | ||||||
| // Copyright (c) Kalinin Andrew. All rights reserved. | ||||||
| // </copyright> | ||||||
|
|
||||||
| namespace CheckAmount; | ||||||
|
|
||||||
| using System.Security.Cryptography; | ||||||
| using System.Text; | ||||||
|
|
||||||
| /// <summary> | ||||||
| /// Calculates the checksum (hash amount) for a given file or directory structure. | ||||||
| /// </summary> | ||||||
| public class CheckAmountСalculator | ||||||
| { | ||||||
| private const int BufferSize = 4096; | ||||||
|
|
||||||
| /// <summary> | ||||||
| /// Initiates the single-threaded checksum calculation. | ||||||
| /// </summary> | ||||||
| /// <param name="path">The path to the file or directory.</param> | ||||||
| /// <returns>The MD5 hash.</returns> | ||||||
| /// <exception cref="FileNotFoundException">Thrown if the path does not exist.</exception> | ||||||
| public string Calculate(string path) | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Потенциально длительные операции в современном мире должны быть асинхронными, а считать хеш точно может быть долго. |
||||||
| { | ||||||
| byte[] hashBytes; | ||||||
|
|
||||||
| if (File.Exists(path)) | ||||||
| { | ||||||
| hashBytes = this.CalculateFileCheckAmount(path); | ||||||
| } | ||||||
| else if (Directory.Exists(path)) | ||||||
| { | ||||||
| hashBytes = this.CalculateDirectoryCheckAmount(path); | ||||||
| } | ||||||
| else | ||||||
| { | ||||||
| throw new FileNotFoundException("Путь не найден"); | ||||||
| } | ||||||
|
|
||||||
| return BitConverter.ToString(hashBytes).Replace("-", string.Empty).ToLower(); | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Не, всё-таки возвращать хешер должен был хеш, а не его строковое представление. Мы же не знаем, как он может быть использован в дальнейшем. |
||||||
| } | ||||||
|
|
||||||
| /// <summary> | ||||||
| /// Initiates the multi-threaded checksum calculation for directories. | ||||||
| /// </summary> | ||||||
| /// <param name="path">The path to the file or directory.</param> | ||||||
| /// <returns>The MD5 hash.</returns> | ||||||
| /// <exception cref="FileNotFoundException">Thrown if the path does not exist.</exception> | ||||||
| public string CalculateMultiThreaded(string path) | ||||||
| { | ||||||
| byte[] hashBytes; | ||||||
|
|
||||||
| if (File.Exists(path)) | ||||||
| { | ||||||
| hashBytes = this.CalculateFileCheckAmount(path); | ||||||
| } | ||||||
| else if (Directory.Exists(path)) | ||||||
| { | ||||||
| hashBytes = this.CalculateDirectoryAmountThreaded(path); | ||||||
| } | ||||||
| else | ||||||
| { | ||||||
| throw new FileNotFoundException("Путь не найден"); | ||||||
| } | ||||||
|
|
||||||
| return BitConverter.ToString(hashBytes).Replace("-", string.Empty).ToLower(); | ||||||
| } | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Некий копипаст с предыдущим методом. Можно было бы сделать один метод и параметризовать его лямбдой. И сделать два метода-обёртки, чтобы клиенту не надо было лямбду передавать. |
||||||
|
|
||||||
| private byte[] CalculateFileCheckAmount(string filePath) | ||||||
| { | ||||||
| string fileName = Path.GetFileName(filePath); | ||||||
| byte[] fileNameBytes = Encoding.UTF8.GetBytes(fileName); | ||||||
| using (MD5 md = MD5.Create()) | ||||||
| using (FileStream fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read)) | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
Древний C#, значит генерено нейросетью :) |
||||||
| { | ||||||
| md.TransformBlock(fileNameBytes, 0, fileNameBytes.Length, fileNameBytes, 0); | ||||||
|
|
||||||
| byte[] buffer = new byte[BufferSize]; | ||||||
| int bytesRead; | ||||||
|
|
||||||
| while ((bytesRead = fileStream.Read(buffer, 0, BufferSize)) > 0) | ||||||
| { | ||||||
| md.TransformBlock(buffer, 0, bytesRead, buffer, 0); | ||||||
| } | ||||||
|
|
||||||
| md.TransformFinalBlock(new byte[0], 0, 0); | ||||||
| return md.Hash!; | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||||||
| } | ||||||
| } | ||||||
|
|
||||||
| private byte[] CalculateDirectoryCheckAmount(string dirPath) | ||||||
| { | ||||||
| string dirName = new DirectoryInfo(dirPath).Name; | ||||||
| byte[] dirNameBytes = Encoding.UTF8.GetBytes(dirName); | ||||||
|
|
||||||
| string[] subDirs = Directory.GetDirectories(dirPath); | ||||||
| string[] files = Directory.GetFiles(dirPath); | ||||||
|
|
||||||
| Array.Sort(subDirs); | ||||||
| Array.Sort(files); | ||||||
|
|
||||||
| using (MemoryStream stream = new MemoryStream()) | ||||||
| { | ||||||
| stream.Write(dirNameBytes, 0, dirNameBytes.Length); | ||||||
|
|
||||||
| foreach (string subDir in subDirs) | ||||||
| { | ||||||
| byte[] subDirHash = this.CalculateDirectoryCheckAmount(subDir); | ||||||
| stream.Write(subDirHash, 0, subDirHash.Length); | ||||||
| } | ||||||
|
|
||||||
| foreach (string file in files) | ||||||
| { | ||||||
| byte[] fileHash = this.CalculateFileCheckAmount(file); | ||||||
| stream.Write(fileHash, 0, fileHash.Length); | ||||||
| } | ||||||
|
|
||||||
| stream.Position = 0; | ||||||
| using (MD5 md = MD5.Create()) | ||||||
| { | ||||||
| return md.ComputeHash(stream); | ||||||
| } | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Есть статическая версия ComputeHash (правда, называется HashData), которая позволила бы это записать в одну строчку. Объект-то Вам тут не нужен. |
||||||
| } | ||||||
| } | ||||||
|
|
||||||
| private byte[] CalculateDirectoryAmountThreaded(string dirPath) | ||||||
| { | ||||||
| string dirName = new DirectoryInfo(dirPath).Name; | ||||||
| byte[] dirNameBytes = Encoding.UTF8.GetBytes(dirName); | ||||||
|
|
||||||
| string[] subDirs = Directory.GetDirectories(dirPath); | ||||||
| string[] files = Directory.GetFiles(dirPath); | ||||||
|
|
||||||
| Array.Sort(subDirs); | ||||||
| Array.Sort(files); | ||||||
|
|
||||||
| List<Task<byte[]>> tasks = new List<Task<byte[]>>(); | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Уже давно в C# не надо писать один тип дважды. Тем более такой. |
||||||
|
|
||||||
| foreach (string subDir in subDirs) | ||||||
| { | ||||||
| tasks.Add(Task.Run(() => this.CalculateDirectoryAmountThreaded(subDir))); | ||||||
| } | ||||||
|
|
||||||
| foreach (string file in files) | ||||||
| { | ||||||
| tasks.Add(Task.Run(() => this.CalculateFileCheckAmount(file))); | ||||||
| } | ||||||
|
|
||||||
| Task.WaitAll(tasks.ToArray()); | ||||||
|
|
||||||
| using (MemoryStream stream = new MemoryStream()) | ||||||
| { | ||||||
| stream.Write(dirNameBytes, 0, dirNameBytes.Length); | ||||||
|
|
||||||
| foreach (Task<byte[]> task in tasks) | ||||||
| { | ||||||
| byte[] resultHash = task.Result; | ||||||
| stream.Write(resultHash, 0, resultHash.Length); | ||||||
| } | ||||||
|
|
||||||
| stream.Position = 0; | ||||||
| using (MD5 md = MD5.Create()) | ||||||
| { | ||||||
| return md.ComputeHash(stream); | ||||||
| } | ||||||
| } | ||||||
| } | ||||||
| } | ||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| // <copyright file="Program.cs" company="Kalinin Andrew"> | ||
| // Copyright (c) Kalinin Andrew. All rights reserved. | ||
| // </copyright> | ||
|
|
||
| using System.Diagnostics; | ||
| using CheckAmount; | ||
|
|
||
| string testDir = @"..\..\..\TestDir"; | ||
|
|
||
| Stopwatch sw = new Stopwatch(); | ||
| CheckAmountСalculator calculator = new CheckAmountСalculator(); | ||
|
|
||
| sw.Start(); | ||
| string checkAmountOneThread = calculator.Calculate(testDir); | ||
| sw.Stop(); | ||
| long timeOne = sw.ElapsedMilliseconds; | ||
| Console.WriteLine($"Хеш: {checkAmountOneThread}"); | ||
| Console.WriteLine($"Время(однопоточный запуск): {timeOne} мс"); | ||
|
|
||
| sw.Restart(); | ||
| string checkAmountMulti = calculator.CalculateMultiThreaded(testDir); | ||
| sw.Stop(); | ||
| long timeMulti = sw.ElapsedMilliseconds; | ||
| Console.WriteLine($"Хеш: {checkAmountMulti}"); | ||
| Console.WriteLine($"Время(однопоточный запуск): {timeMulti} мс"); | ||
|
|
||
| Console.WriteLine($"Хеши совпадают {checkAmountMulti == checkAmountOneThread}"); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| dajskdhasjkdhfsdkfnkdsngkdfnfk;fksdfnkjsdfhkjdsnfksdanfskl |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| dasdasfgfdgsddsfbskdfns |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| { | ||
| "$schema": "https://raw.githubusercontent.com/DotNetAnalyzers/StyleCopAnalyzers/master/StyleCop.Analyzers/StyleCop.Analyzers/Settings/stylecop.schema.json", | ||
| "settings": { | ||
| "documentationRules": { | ||
| "companyName": "Kalinin Andrew" | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Вроде как у него нет никаких причин быть не static