mirror of
https://github.com/oliverbooth/fdup.git
synced 2024-11-14 04:15:41 +00:00
Compare commits
No commits in common. "801dfe09cb005256b157348dd470142edb59804e" and "16eb21e8794cf7d578eda69eb4620fb9ba1c14af" have entirely different histories.
801dfe09cb
...
16eb21e879
@ -1,26 +0,0 @@
|
|||||||
using System.ComponentModel;
|
|
||||||
using Humanizer;
|
|
||||||
using Spectre.Console;
|
|
||||||
using Spectre.Console.Cli;
|
|
||||||
|
|
||||||
namespace FindDuplicates;
|
|
||||||
|
|
||||||
[Description("Display a list of usable hashing algorithms.")]
|
|
||||||
internal sealed class AlgListCommand : Command
|
|
||||||
{
|
|
||||||
public override int Execute(CommandContext context)
|
|
||||||
{
|
|
||||||
AnsiConsole.WriteLine("The default algorithm fdup uses is SHA512.");
|
|
||||||
AnsiConsole.MarkupLine("To specify a different one, use the [cyan]-a[/] or [cyan]--algorithm[/] flag, and pass one of the values below:");
|
|
||||||
|
|
||||||
var table = new Table();
|
|
||||||
table.AddColumn("Algorithm");
|
|
||||||
table.AddColumn("Value");
|
|
||||||
|
|
||||||
foreach (Algorithm algorithm in Enum.GetValues<Algorithm>())
|
|
||||||
table.AddRow($"{algorithm.Humanize()}", $"{algorithm.ToString().ToLower()}");
|
|
||||||
|
|
||||||
AnsiConsole.Write(table);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,15 +0,0 @@
|
|||||||
using System.ComponentModel;
|
|
||||||
|
|
||||||
namespace FindDuplicates;
|
|
||||||
|
|
||||||
internal enum Algorithm
|
|
||||||
{
|
|
||||||
[Description("SHA512")] Sha512,
|
|
||||||
[Description("SHA384")] Sha384,
|
|
||||||
[Description("SHA256")] Sha256,
|
|
||||||
[Description("SHA3-512")] Sha3512,
|
|
||||||
[Description("SHA3-384")] Sha3384,
|
|
||||||
[Description("SHA3-256")] Sha3256,
|
|
||||||
[Description("SHA1")] Sha1,
|
|
||||||
[Description("MD5")] Md5
|
|
||||||
}
|
|
@ -1,42 +0,0 @@
|
|||||||
using System.Security.Cryptography;
|
|
||||||
|
|
||||||
namespace FindDuplicates;
|
|
||||||
|
|
||||||
internal static class AlgorithmExtensions
|
|
||||||
{
|
|
||||||
public static int GetByteCount(this Algorithm algorithm)
|
|
||||||
{
|
|
||||||
return algorithm switch
|
|
||||||
{
|
|
||||||
Algorithm.Sha512 => SHA512.HashSizeInBytes,
|
|
||||||
Algorithm.Sha384 => SHA384.HashSizeInBytes,
|
|
||||||
Algorithm.Sha256 => SHA256.HashSizeInBytes,
|
|
||||||
Algorithm.Sha3512 => SHA3_512.HashSizeInBytes,
|
|
||||||
Algorithm.Sha3384 => SHA3_384.HashSizeInBytes,
|
|
||||||
Algorithm.Sha3256 => SHA3_256.HashSizeInBytes,
|
|
||||||
Algorithm.Sha1 => SHA1.HashSizeInBytes,
|
|
||||||
Algorithm.Md5 => MD5.HashSizeInBytes,
|
|
||||||
_ => 0
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int HashData(this Algorithm algorithm, Stream source, Span<byte> destination)
|
|
||||||
{
|
|
||||||
// I'd love to use a dictionary to cache the function map, but you can't use Span<> as a type argument,
|
|
||||||
// probably due to the fact that a lambda heap allocs, and we can't have that for ref structs, oh no (!)
|
|
||||||
// so enjoy this absolutely cursed switch expression which checks each algorithm separately. I hate it too.
|
|
||||||
|
|
||||||
return algorithm switch
|
|
||||||
{
|
|
||||||
Algorithm.Sha512 => SHA512.HashData(source, destination),
|
|
||||||
Algorithm.Sha384 => SHA384.HashData(source, destination),
|
|
||||||
Algorithm.Sha256 => SHA256.HashData(source, destination),
|
|
||||||
Algorithm.Sha3512 => SHA3_512.HashData(source, destination),
|
|
||||||
Algorithm.Sha3384 => SHA3_384.HashData(source, destination),
|
|
||||||
Algorithm.Sha3256 => SHA3_256.HashData(source, destination),
|
|
||||||
Algorithm.Sha1 => SHA1.HashData(source, destination),
|
|
||||||
Algorithm.Md5 => MD5.HashData(source, destination),
|
|
||||||
_ => -1
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
@ -33,7 +33,6 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Humanizer.Core" Version="2.14.1" />
|
|
||||||
<PackageReference Include="Spectre.Console.Cli" Version="0.48.0"/>
|
<PackageReference Include="Spectre.Console.Cli" Version="0.48.0"/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
|
using System.Security.Cryptography;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using Humanizer;
|
|
||||||
using Spectre.Console;
|
using Spectre.Console;
|
||||||
using Spectre.Console.Cli;
|
using Spectre.Console.Cli;
|
||||||
|
|
||||||
@ -21,7 +21,6 @@ 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")}[/]");
|
||||||
AnsiConsole.MarkupLine($"Using hash algorithm [cyan]{settings.Algorithm.Humanize()}[/]");
|
|
||||||
|
|
||||||
await AnsiConsole.Status()
|
await AnsiConsole.Status()
|
||||||
.StartAsync("Waiting to hash files...", DoHashWaitAsync)
|
.StartAsync("Waiting to hash files...", DoHashWaitAsync)
|
||||||
@ -34,17 +33,17 @@ internal sealed class ListCommand : AsyncCommand<ListSettings>
|
|||||||
{
|
{
|
||||||
int fileCount = files.Count;
|
int fileCount = files.Count;
|
||||||
|
|
||||||
if (fileCount <= 1)
|
if (fileCount > 1)
|
||||||
continue;
|
{
|
||||||
|
duplicates += fileCount;
|
||||||
|
AnsiConsole.MarkupLineInterpolated($"Found [cyan]{fileCount}[/] identical files");
|
||||||
|
AnsiConsole.MarkupLineInterpolated($"SHA512 [green]{hash}[/]:");
|
||||||
|
|
||||||
duplicates += fileCount;
|
foreach (FileInfo file in files)
|
||||||
AnsiConsole.MarkupLineInterpolated($"Found [cyan]{fileCount}[/] identical files");
|
AnsiConsole.MarkupLineInterpolated($"- {file.FullName}");
|
||||||
AnsiConsole.MarkupLineInterpolated($"{settings.Algorithm.Humanize()} [green]{hash}[/]:");
|
|
||||||
|
|
||||||
foreach (FileInfo file in files)
|
AnsiConsole.WriteLine();
|
||||||
AnsiConsole.MarkupLineInterpolated($"- {file.FullName}");
|
}
|
||||||
|
|
||||||
AnsiConsole.WriteLine();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (duplicates == 0)
|
if (duplicates == 0)
|
||||||
@ -110,12 +109,12 @@ internal sealed class ListCommand : AsyncCommand<ListSettings>
|
|||||||
|
|
||||||
private void ProcessFile(FileInfo file, ListSettings settings)
|
private void ProcessFile(FileInfo file, ListSettings settings)
|
||||||
{
|
{
|
||||||
Span<byte> buffer = stackalloc byte[settings.Algorithm.GetByteCount()];
|
Span<byte> buffer = stackalloc byte[64];
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
using FileStream stream = file.OpenRead();
|
using FileStream stream = file.OpenRead();
|
||||||
using BufferedStream bufferedStream = new BufferedStream(stream, 1048576 /* 1MB */);
|
using BufferedStream bufferedStream = new BufferedStream(stream, 1048576 /* 1MB */);
|
||||||
settings.Algorithm.HashData(bufferedStream, buffer);
|
SHA512.HashData(bufferedStream, buffer);
|
||||||
string hash = ByteSpanToString(buffer);
|
string hash = ByteSpanToString(buffer);
|
||||||
if (settings.Verbose)
|
if (settings.Verbose)
|
||||||
AnsiConsole.WriteLine($"{file.FullName} ->\n {hash}");
|
AnsiConsole.WriteLine($"{file.FullName} ->\n {hash}");
|
||||||
@ -147,7 +146,7 @@ internal sealed class ListCommand : AsyncCommand<ListSettings>
|
|||||||
|
|
||||||
private static string ByteSpanToString(ReadOnlySpan<byte> buffer)
|
private static string ByteSpanToString(ReadOnlySpan<byte> buffer)
|
||||||
{
|
{
|
||||||
var builder = new StringBuilder(buffer.Length * 2);
|
var builder = new StringBuilder();
|
||||||
|
|
||||||
foreach (byte b in buffer)
|
foreach (byte b in buffer)
|
||||||
builder.Append($"{b:X2}");
|
builder.Append($"{b:X2}");
|
||||||
|
@ -10,13 +10,8 @@ internal sealed class ListSettings : CommandSettings
|
|||||||
[DefaultValue(".")]
|
[DefaultValue(".")]
|
||||||
public string InputPath { get; set; } = ".";
|
public string InputPath { get; set; } = ".";
|
||||||
|
|
||||||
[CommandOption("-a|--algorithm <algorithm>")]
|
|
||||||
[Description("The hash algorithm used for comparison. Defaults to SHA512. For a list of all available algorithms, run fdup alglist")]
|
|
||||||
[DefaultValue(Algorithm.Sha512)]
|
|
||||||
public Algorithm Algorithm { get; set; } = Algorithm.Sha512;
|
|
||||||
|
|
||||||
[CommandOption("-r|--recursive")]
|
[CommandOption("-r|--recursive")]
|
||||||
[Description("Scans the directory recursively. This may increase run time and is not advised to use when at high order directories such as C: or /")]
|
[Description("When this flag is set, the directory will be scanned recursively. This may take longer.")]
|
||||||
[DefaultValue(false)]
|
[DefaultValue(false)]
|
||||||
public bool Recursive { get; set; } = false;
|
public bool Recursive { get; set; } = false;
|
||||||
|
|
||||||
|
@ -2,5 +2,4 @@ using FindDuplicates;
|
|||||||
using Spectre.Console.Cli;
|
using Spectre.Console.Cli;
|
||||||
|
|
||||||
var app = new CommandApp<ListCommand>();
|
var app = new CommandApp<ListCommand>();
|
||||||
app.Configure(cfg => cfg.AddCommand<AlgListCommand>("alglist"));
|
|
||||||
await app.RunAsync(args).ConfigureAwait(false);
|
await app.RunAsync(args).ConfigureAwait(false);
|
||||||
|
Loading…
Reference in New Issue
Block a user