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.
d2ca59e174
...
9f7e605d38
@ -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>
|
||||||
|
|
||||||
|
@ -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)
|
||||||
|
Loading…
Reference in New Issue
Block a user