1
0
mirror of https://github.com/oliverbooth/fdup.git synced 2024-11-10 03:25:41 +00:00

Compare commits

..

No commits in common. "d2ca59e174ca9cd3392c46d0249a3023089a1188" and "9f7e605d386007082e1597a9a7157dfb95e9f217" have entirely different histories.

2 changed files with 20 additions and 66 deletions

View File

@ -6,7 +6,7 @@
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<AssemblyName>fdup</AssemblyName> <AssemblyName>fdup</AssemblyName>
<VersionPrefix>1.1.0</VersionPrefix> <VersionPrefix>1.0.1</VersionPrefix>
<Authors>Oliver Booth</Authors> <Authors>Oliver Booth</Authors>
</PropertyGroup> </PropertyGroup>

View File

@ -1,6 +1,5 @@
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Diagnostics; using System.Diagnostics;
using System.Security;
using System.Security.Cryptography; using System.Security.Cryptography;
using System.Text; using System.Text;
using Spectre.Console; using Spectre.Console;
@ -24,9 +23,7 @@ internal sealed class ListCommand : AsyncCommand<ListSettings>
AnsiConsole.MarkupLineInterpolated($"Searching [cyan]{inputDirectory.FullName}[/]"); AnsiConsole.MarkupLineInterpolated($"Searching [cyan]{inputDirectory.FullName}[/]");
AnsiConsole.MarkupLine($"Recursive mode is {(settings.Recursive ? "[green]ON" : "[red]OFF")}[/]"); AnsiConsole.MarkupLine($"Recursive mode is {(settings.Recursive ? "[green]ON" : "[red]OFF")}[/]");
await AnsiConsole.Status() await SearchAsync(inputDirectory, settings);
.StartAsync("Waiting to hash files...", DoHashWaitAsync)
.ConfigureAwait(false);
AnsiConsole.WriteLine(); AnsiConsole.WriteLine();
@ -54,35 +51,11 @@ internal sealed class ListCommand : AsyncCommand<ListSettings>
AnsiConsole.MarkupLineInterpolated($"[yellow]Found [cyan]{duplicates}[/] duplicates![/]"); AnsiConsole.MarkupLineInterpolated($"[yellow]Found [cyan]{duplicates}[/] duplicates![/]");
return 0; return 0;
async Task DoHashWaitAsync(StatusContext ctx)
{
await WaitForHashCompletionAsync(settings, inputDirectory, ctx);
}
} }
private async Task WaitForHashCompletionAsync(ListSettings settings, private async Task SearchAsync(DirectoryInfo inputDirectory, ListSettings settings)
DirectoryInfo inputDirectory,
StatusContext ctx)
{ {
var tasks = new List<Task>(); var tasks = new List<Task>();
SearchDuplicates(inputDirectory, settings, tasks);
await Task.Run(() =>
{
int incompleteTasks;
do
{
incompleteTasks = tasks.Count(t => !t.IsCompleted);
ctx.Status($"Waiting to hash {incompleteTasks} {(incompleteTasks == 1 ? "file" : "files")}...");
ctx.Refresh();
} while (tasks.Count > 0 && incompleteTasks > 0);
ctx.Status("Hash complete");
}).ConfigureAwait(false);
}
private void SearchDuplicates(DirectoryInfo inputDirectory, ListSettings settings, ICollection<Task> tasks)
{
var directoryStack = new Stack<DirectoryInfo>([inputDirectory]); var directoryStack = new Stack<DirectoryInfo>([inputDirectory]);
while (directoryStack.Count > 0) while (directoryStack.Count > 0)
{ {
@ -93,54 +66,35 @@ internal sealed class ListCommand : AsyncCommand<ListSettings>
if (settings.Recursive) if (settings.Recursive)
{ {
try foreach (DirectoryInfo childDirectory in currentDirectory.EnumerateDirectories())
{ directoryStack.Push(childDirectory);
foreach (DirectoryInfo childDirectory in currentDirectory.EnumerateDirectories())
directoryStack.Push(childDirectory);
}
catch (Exception ex)
{
AnsiConsole.MarkupLineInterpolated($"[red]Error:[/] {ex.Message}");
}
} }
try foreach (FileInfo file in currentDirectory.EnumerateFiles())
{ {
foreach (FileInfo file in currentDirectory.EnumerateFiles()) string relativeFilePath = Path.GetRelativePath(inputDirectory.FullName, file.FullName);
{ AnsiConsole.MarkupLineInterpolated($"Checking hash for [cyan]{relativeFilePath}[/]");
string relativeFilePath = Path.GetRelativePath(inputDirectory.FullName, file.FullName); tasks.Add(Task.Run(() => ProcessFile(file)));
AnsiConsole.MarkupLineInterpolated($"Checking hash for [cyan]{relativeFilePath}[/]");
tasks.Add(Task.Run(() => ProcessFile(file)));
}
}
catch (Exception ex)
{
AnsiConsole.MarkupLineInterpolated($"[red]Error:[/] {ex.Message}");
} }
} }
await Task.WhenAll(tasks);
} }
private void ProcessFile(FileInfo file) private void ProcessFile(FileInfo file)
{ {
Span<byte> buffer = stackalloc byte[64]; Span<byte> buffer = stackalloc byte[64];
try using FileStream stream = file.OpenRead();
{ using BufferedStream bufferedStream = new BufferedStream(stream, 1048576 /* 1MB */);
using FileStream stream = file.OpenRead(); SHA512.HashData(bufferedStream, buffer);
using BufferedStream bufferedStream = new BufferedStream(stream, 1048576 /* 1MB */); string hash = ByteSpanToString(buffer);
SHA512.HashData(bufferedStream, buffer); Trace.WriteLine($"{file.FullName}: {hash}");
string hash = ByteSpanToString(buffer);
Trace.WriteLine($"{file.FullName}: {hash}");
if (!_fileHashMap.TryGetValue(hash, out List<FileInfo>? cache)) if (!_fileHashMap.TryGetValue(hash, out List<FileInfo>? cache))
_fileHashMap[hash] = cache = new List<FileInfo>(); _fileHashMap[hash] = cache = new List<FileInfo>();
lock (cache) lock (cache)
cache.Add(file); cache.Add(file);
}
catch (Exception ex)
{
AnsiConsole.MarkupLineInterpolated($"[red]Error:[/] {ex.Message}");
}
} }
private static string ByteSpanToString(ReadOnlySpan<byte> buffer) private static string ByteSpanToString(ReadOnlySpan<byte> buffer)