mirror of
https://github.com/oliverbooth/fdup.git
synced 2024-11-10 03:05:42 +00:00
Compare commits
3 Commits
16eb21e879
...
801dfe09cb
Author | SHA1 | Date | |
---|---|---|---|
801dfe09cb | |||
01e06a46cf | |||
dd683c2bf3 |
26
FindDuplicates/AlgListCommand.cs
Normal file
26
FindDuplicates/AlgListCommand.cs
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
15
FindDuplicates/Algorithm.cs
Normal file
15
FindDuplicates/Algorithm.cs
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
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
|
||||||
|
}
|
42
FindDuplicates/AlgorithmExtensions.cs
Normal file
42
FindDuplicates/AlgorithmExtensions.cs
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
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,6 +33,7 @@
|
|||||||
</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,6 +21,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")}[/]");
|
||||||
|
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)
|
||||||
@ -33,18 +34,18 @@ internal sealed class ListCommand : AsyncCommand<ListSettings>
|
|||||||
{
|
{
|
||||||
int fileCount = files.Count;
|
int fileCount = files.Count;
|
||||||
|
|
||||||
if (fileCount > 1)
|
if (fileCount <= 1)
|
||||||
{
|
continue;
|
||||||
|
|
||||||
duplicates += fileCount;
|
duplicates += fileCount;
|
||||||
AnsiConsole.MarkupLineInterpolated($"Found [cyan]{fileCount}[/] identical files");
|
AnsiConsole.MarkupLineInterpolated($"Found [cyan]{fileCount}[/] identical files");
|
||||||
AnsiConsole.MarkupLineInterpolated($"SHA512 [green]{hash}[/]:");
|
AnsiConsole.MarkupLineInterpolated($"{settings.Algorithm.Humanize()} [green]{hash}[/]:");
|
||||||
|
|
||||||
foreach (FileInfo file in files)
|
foreach (FileInfo file in files)
|
||||||
AnsiConsole.MarkupLineInterpolated($"- {file.FullName}");
|
AnsiConsole.MarkupLineInterpolated($"- {file.FullName}");
|
||||||
|
|
||||||
AnsiConsole.WriteLine();
|
AnsiConsole.WriteLine();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (duplicates == 0)
|
if (duplicates == 0)
|
||||||
AnsiConsole.MarkupLine("[green]No duplicates found![/]");
|
AnsiConsole.MarkupLine("[green]No duplicates found![/]");
|
||||||
@ -109,12 +110,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[64];
|
Span<byte> buffer = stackalloc byte[settings.Algorithm.GetByteCount()];
|
||||||
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 */);
|
||||||
SHA512.HashData(bufferedStream, buffer);
|
settings.Algorithm.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}");
|
||||||
@ -146,7 +147,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();
|
var builder = new StringBuilder(buffer.Length * 2);
|
||||||
|
|
||||||
foreach (byte b in buffer)
|
foreach (byte b in buffer)
|
||||||
builder.Append($"{b:X2}");
|
builder.Append($"{b:X2}");
|
||||||
|
@ -10,8 +10,13 @@ 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("When this flag is set, the directory will be scanned recursively. This may take longer.")]
|
[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 /")]
|
||||||
[DefaultValue(false)]
|
[DefaultValue(false)]
|
||||||
public bool Recursive { get; set; } = false;
|
public bool Recursive { get; set; } = false;
|
||||||
|
|
||||||
|
@ -2,4 +2,5 @@ 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