mirror of
https://github.com/oliverbooth/fdup.git
synced 2024-11-09 23:45:41 +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>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Humanizer.Core" Version="2.14.1" />
|
||||
<PackageReference Include="Spectre.Console.Cli" Version="0.48.0"/>
|
||||
</ItemGroup>
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
using System.Collections.Concurrent;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using Humanizer;
|
||||
using Spectre.Console;
|
||||
using Spectre.Console.Cli;
|
||||
|
||||
@ -21,6 +21,7 @@ internal sealed class ListCommand : AsyncCommand<ListSettings>
|
||||
|
||||
AnsiConsole.MarkupLineInterpolated($"Searching [cyan]{inputDirectory.FullName}[/]");
|
||||
AnsiConsole.MarkupLine($"Recursive mode is {(settings.Recursive ? "[green]ON" : "[red]OFF")}[/]");
|
||||
AnsiConsole.MarkupLine($"Using hash algorithm [cyan]{settings.Algorithm.Humanize()}[/]");
|
||||
|
||||
await AnsiConsole.Status()
|
||||
.StartAsync("Waiting to hash files...", DoHashWaitAsync)
|
||||
@ -33,18 +34,18 @@ internal sealed class ListCommand : AsyncCommand<ListSettings>
|
||||
{
|
||||
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}[/]:");
|
||||
AnsiConsole.MarkupLineInterpolated($"{settings.Algorithm.Humanize()} [green]{hash}[/]:");
|
||||
|
||||
foreach (FileInfo file in files)
|
||||
AnsiConsole.MarkupLineInterpolated($"- {file.FullName}");
|
||||
|
||||
AnsiConsole.WriteLine();
|
||||
}
|
||||
}
|
||||
|
||||
if (duplicates == 0)
|
||||
AnsiConsole.MarkupLine("[green]No duplicates found![/]");
|
||||
@ -109,12 +110,12 @@ internal sealed class ListCommand : AsyncCommand<ListSettings>
|
||||
|
||||
private void ProcessFile(FileInfo file, ListSettings settings)
|
||||
{
|
||||
Span<byte> buffer = stackalloc byte[64];
|
||||
Span<byte> buffer = stackalloc byte[settings.Algorithm.GetByteCount()];
|
||||
try
|
||||
{
|
||||
using FileStream stream = file.OpenRead();
|
||||
using BufferedStream bufferedStream = new BufferedStream(stream, 1048576 /* 1MB */);
|
||||
SHA512.HashData(bufferedStream, buffer);
|
||||
settings.Algorithm.HashData(bufferedStream, buffer);
|
||||
string hash = ByteSpanToString(buffer);
|
||||
if (settings.Verbose)
|
||||
AnsiConsole.WriteLine($"{file.FullName} ->\n {hash}");
|
||||
@ -146,7 +147,7 @@ internal sealed class ListCommand : AsyncCommand<ListSettings>
|
||||
|
||||
private static string ByteSpanToString(ReadOnlySpan<byte> buffer)
|
||||
{
|
||||
var builder = new StringBuilder();
|
||||
var builder = new StringBuilder(buffer.Length * 2);
|
||||
|
||||
foreach (byte b in buffer)
|
||||
builder.Append($"{b:X2}");
|
||||
|
@ -10,8 +10,13 @@ internal sealed class ListSettings : CommandSettings
|
||||
[DefaultValue(".")]
|
||||
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")]
|
||||
[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)]
|
||||
public bool Recursive { get; set; } = false;
|
||||
|
||||
|
@ -2,4 +2,5 @@ using FindDuplicates;
|
||||
using Spectre.Console.Cli;
|
||||
|
||||
var app = new CommandApp<ListCommand>();
|
||||
app.Configure(cfg => cfg.AddCommand<AlgListCommand>("alglist"));
|
||||
await app.RunAsync(args).ConfigureAwait(false);
|
||||
|
Loading…
Reference in New Issue
Block a user