From 82f24d169ef991df573fb3c584bf065b105a0942 Mon Sep 17 00:00:00 2001 From: Anu Bandi Date: Wed, 10 Dec 2025 22:31:28 -0800 Subject: [PATCH 1/6] got xxhash3 working maybe --- README.md | 2 +- src/HashUtil.Tests/HashTests.fs | 14 +++++++++++--- src/HashUtil/Checksum.fs | 18 +++++++++++------- src/HashUtil/HashUtil.fsproj | 4 +++- src/HashUtil/NonCryptoWrapper.fs | 17 +++++++++++++++++ 5 files changed, 43 insertions(+), 12 deletions(-) create mode 100644 src/HashUtil/NonCryptoWrapper.fs diff --git a/README.md b/README.md index dfee07b..a05a908 100644 --- a/README.md +++ b/README.md @@ -39,7 +39,7 @@ Arguments: Options: -t, --tree Print directory tree -s, --save Save the checksum to a file - -a, --algorithm The hash function to use [default: sha1] + -a, --algorithm The hash function to use [default: sha1] -i, --include-hidden-files Include hidden files -e, --skip-empty-dir Skip empty directories -n, --ignore Directories/files to not include diff --git a/src/HashUtil.Tests/HashTests.fs b/src/HashUtil.Tests/HashTests.fs index 1628796..142ad26 100644 --- a/src/HashUtil.Tests/HashTests.fs +++ b/src/HashUtil.Tests/HashTests.fs @@ -19,7 +19,10 @@ type ChecksumTests(output: ITestOutputHelper) = [| SHA512 "cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e" |] [| BLAKE3 - "af1349b9f5f9a1a6a0404dea36dcc9499bcb25c9adc112b7cc9a93cae41f3262" |] ] + "af1349b9f5f9a1a6a0404dea36dcc9499bcb25c9adc112b7cc9a93cae41f3262" |] + [| XXHASH3 + "2d06800538d394c2" |] + ] [] member _.``hash empty string``(hashType, expectedHash) = @@ -45,7 +48,11 @@ type ChecksumTests(output: ITestOutputHelper) = "9b71d224bd62f3785d96d46ad3ea3d73319bfbc2890caadae2dff72519673ca72323c3d99ba5c11d7c7acc6e14b8c5da0c4663475c2e5c3adef46f73bcdec043" |] [| BLAKE3 "hello" - "ea8f163db38682925e4491c5e58d4bb3506ef8c14eb78a86e908c5624a67200f" |] ] + "ea8f163db38682925e4491c5e58d4bb3506ef8c14eb78a86e908c5624a67200f" |] + [| XXHASH3 + "hello" + "9555e8555c62dcfd" |] + ] [] @@ -64,7 +71,8 @@ type ChecksumTests(output: ITestOutputHelper) = [| [ "sha384"; " Sha384 "; "SHA384 " ]; Some SHA384 |] [| [ "sha512"; " Sha512 "; "SHA512 " ]; Some SHA512 |] [| [ "blake3"; " Blake3 "; "BLAKE3 " ]; Some BLAKE3 |] - [| [ "asha1"; " md6 "; "SHA513 " ]; None |] ] + [| [ "xxhash3"; " xxHash3 "; "XXHASH3 " ]; Some XXHASH3 |] + [| [ "asna1"; " md6 "; "SHA513 " ]; None |] ] [] member _.``parse HashType``(inputStrs, expectedType) = diff --git a/src/HashUtil/Checksum.fs b/src/HashUtil/Checksum.fs index 8a7b88e..6bd0859 100644 --- a/src/HashUtil/Checksum.fs +++ b/src/HashUtil/Checksum.fs @@ -2,6 +2,7 @@ open Microsoft.FSharp.Reflection open System.IO +open System.IO.Hashing open System.Security.Cryptography open System.Text open Blake3 @@ -15,6 +16,7 @@ module Checksum = | SHA384 | SHA512 | BLAKE3 + | XXHASH3 let allHashTypes: HashType[] = typeof @@ -33,14 +35,16 @@ module Checksum = | "SHA384" -> Some SHA384 | "SHA512" -> Some SHA512 | "BLAKE3" -> Some BLAKE3 + | "XXHASH3" -> Some XXHASH3 | _ -> None let getHashAlgorithm hashType : HashAlgorithm = match hashType with - | MD5 -> upcast MD5.Create() - | RIPEMD160 -> upcast Checksums.RIPEMD160.Create() - | SHA1 -> upcast SHA1.Create() - | SHA256 -> upcast SHA256.Create() - | SHA384 -> upcast SHA384.Create() - | SHA512 -> upcast SHA512.Create() - | BLAKE3 -> upcast (new Blake3.Blake3HashAlgorithm()) + | MD5 -> MD5.Create() + | RIPEMD160 -> Checksums.RIPEMD160.Create() + | SHA1 -> SHA1.Create() + | SHA256 -> SHA256.Create() + | SHA384 -> SHA384.Create() + | SHA512 -> SHA512.Create() + | BLAKE3 -> (new Blake3.Blake3HashAlgorithm()) + | XXHASH3 -> (new NonCryptoWrapper(new System.IO.Hashing.XxHash3())) diff --git a/src/HashUtil/HashUtil.fsproj b/src/HashUtil/HashUtil.fsproj index 565a7d7..415605e 100644 --- a/src/HashUtil/HashUtil.fsproj +++ b/src/HashUtil/HashUtil.fsproj @@ -30,6 +30,7 @@ + @@ -38,9 +39,10 @@ - + + diff --git a/src/HashUtil/NonCryptoWrapper.fs b/src/HashUtil/NonCryptoWrapper.fs new file mode 100644 index 0000000..0ca80f1 --- /dev/null +++ b/src/HashUtil/NonCryptoWrapper.fs @@ -0,0 +1,17 @@ +namespace HashUtil + +open System +open System.IO.Hashing +open System.Security.Cryptography + +type NonCryptoWrapper(hashAlgo: NonCryptographicHashAlgorithm) = + inherit HashAlgorithm() + + override this.HashCore(array: byte[], ibStart: int, cbSize: int) = + hashAlgo.Append(System.ArraySegment(array, ibStart, cbSize)) + + override this.HashFinal() = + hashAlgo.GetCurrentHash() + + override this.Initialize() = + hashAlgo.Reset() \ No newline at end of file From a8cdb8dcb922e6c989ec1479330c227aa1067008 Mon Sep 17 00:00:00 2001 From: Anu Bandi Date: Wed, 10 Dec 2025 22:54:52 -0800 Subject: [PATCH 2/6] formatted --- src/HashUtil.Tests/HashTests.fs | 9 ++------- src/HashUtil/Checksum.fs | 16 ++++++++-------- src/HashUtil/NonCryptoWrapper.fs | 6 ++---- 3 files changed, 12 insertions(+), 19 deletions(-) diff --git a/src/HashUtil.Tests/HashTests.fs b/src/HashUtil.Tests/HashTests.fs index 142ad26..caa0a0e 100644 --- a/src/HashUtil.Tests/HashTests.fs +++ b/src/HashUtil.Tests/HashTests.fs @@ -20,9 +20,7 @@ type ChecksumTests(output: ITestOutputHelper) = "cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e" |] [| BLAKE3 "af1349b9f5f9a1a6a0404dea36dcc9499bcb25c9adc112b7cc9a93cae41f3262" |] - [| XXHASH3 - "2d06800538d394c2" |] - ] + [| XXHASH3; "2d06800538d394c2" |] ] [] member _.``hash empty string``(hashType, expectedHash) = @@ -49,10 +47,7 @@ type ChecksumTests(output: ITestOutputHelper) = [| BLAKE3 "hello" "ea8f163db38682925e4491c5e58d4bb3506ef8c14eb78a86e908c5624a67200f" |] - [| XXHASH3 - "hello" - "9555e8555c62dcfd" |] - ] + [| XXHASH3; "hello"; "9555e8555c62dcfd" |] ] [] diff --git a/src/HashUtil/Checksum.fs b/src/HashUtil/Checksum.fs index 6bd0859..8e12547 100644 --- a/src/HashUtil/Checksum.fs +++ b/src/HashUtil/Checksum.fs @@ -40,11 +40,11 @@ module Checksum = let getHashAlgorithm hashType : HashAlgorithm = match hashType with - | MD5 -> MD5.Create() - | RIPEMD160 -> Checksums.RIPEMD160.Create() - | SHA1 -> SHA1.Create() - | SHA256 -> SHA256.Create() - | SHA384 -> SHA384.Create() - | SHA512 -> SHA512.Create() - | BLAKE3 -> (new Blake3.Blake3HashAlgorithm()) - | XXHASH3 -> (new NonCryptoWrapper(new System.IO.Hashing.XxHash3())) + | MD5 -> MD5.Create() + | RIPEMD160 -> Checksums.RIPEMD160.Create() + | SHA1 -> SHA1.Create() + | SHA256 -> SHA256.Create() + | SHA384 -> SHA384.Create() + | SHA512 -> SHA512.Create() + | BLAKE3 -> new Blake3.Blake3HashAlgorithm() + | XXHASH3 -> new NonCryptoWrapper(new System.IO.Hashing.XxHash3()) diff --git a/src/HashUtil/NonCryptoWrapper.fs b/src/HashUtil/NonCryptoWrapper.fs index 0ca80f1..b829880 100644 --- a/src/HashUtil/NonCryptoWrapper.fs +++ b/src/HashUtil/NonCryptoWrapper.fs @@ -10,8 +10,6 @@ type NonCryptoWrapper(hashAlgo: NonCryptographicHashAlgorithm) = override this.HashCore(array: byte[], ibStart: int, cbSize: int) = hashAlgo.Append(System.ArraySegment(array, ibStart, cbSize)) - override this.HashFinal() = - hashAlgo.GetCurrentHash() + override this.HashFinal() = hashAlgo.GetCurrentHash() - override this.Initialize() = - hashAlgo.Reset() \ No newline at end of file + override this.Initialize() = hashAlgo.Reset() From 19d98ada470c12ed57f329f7767c8553db56b785 Mon Sep 17 00:00:00 2001 From: Anu Bandi Date: Wed, 10 Dec 2025 23:02:06 -0800 Subject: [PATCH 3/6] updated agents file --- AGENTS.md | 28 ++++++++++++++++++++++++---- README.md | 2 +- src/HashUtil.Tests/HashTests.fs | 7 +++++-- src/HashUtil/Checksum.fs | 7 ++++++- 4 files changed, 36 insertions(+), 8 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index eae3135..0d8d1c3 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -2,13 +2,33 @@ Hashdir is a dotnet (f# language) CLI tool to compute the hash or checksum of a directory. This is done recursively using the directories, files and their names within. This is useful for quickly comparing directories and also determining if anything in the filesystem has changed. -It is written in f# language (part of dotnet 8). +It is written in F# on .NET 8. + +The tool supports many popular hashing algorithms such as `blake3`, `md5`, `ripemd160`, `sha1`, `sha256`, `sha384`, `sha512`, and `xxhash3`. # How to Run -- **Dev Build:** `dotnet build` can be used to quickly make sure the app builds (safe for LLMs) -- **Run Tests:** `dotnet test` can be used to make sure the tests are passing (safe for LLMs) +- **Dev Build:** `dotnet build` can be used to quickly make sure the app builds. +- **Run Tests:** `dotnet test` can be used to make sure the tests are passing. +- **Check changes/PR:** `git diff main` can be used to understand the feature branch. # Project Structure -- `src/` contains all the source code +The solution is organized into several projects within the `src/` directory. + +- **`src/App`**: The main CLI application project. + - `Program.fs`: The entry point of the application, responsible for parsing command-line arguments and orchestrating the hashing process. + +- **`src/HashUtil`**: A library project containing the core hashing logic. + - `Checksum.fs`: This is a key file. It defines the supported hash algorithms (`HashType` discriminated union), a function to parse the algorithm from user input (`parseHashType`), and a factory function to create the appropriate `HashAlgorithm` instance (`getHashAlgorithm`). When adding a new algorithm, this file is the primary one to modify. + - `NonCryptoWrapper.fs`: A wrapper class that adapts non-cryptographic hash algorithms (like `XxHash3`) to the standard `HashAlgorithm` interface, allowing them to be used seamlessly with the existing hashing infrastructure. + - `Hashing.fs`: Contains the logic for hashing files and directories. + - `Library.fs`: Provides a programmatic API for the hashing functionality. + - `Util.fs`: Contains utility functions, including `computeHashOfString`. + +- **`src/Checksums`**: A C# project that provides an implementation of the `RIPEMD160` algorithm. + +- **`src/App.Tests`**: Contains tests for the `App` project. + +- **`src/HashUtil.Tests`**: Contains tests for the `HashUtil` library. + - `HashTests.fs`: Contains tests for the hashing functionality, including tests for different algorithms and input strings. When adding a new algorithm, this file should be updated with new test cases. diff --git a/README.md b/README.md index a05a908..ac3edb1 100644 --- a/README.md +++ b/README.md @@ -39,7 +39,7 @@ Arguments: Options: -t, --tree Print directory tree -s, --save Save the checksum to a file - -a, --algorithm The hash function to use [default: sha1] + -a, --algorithm The hash function to use [default: sha1] -i, --include-hidden-files Include hidden files -e, --skip-empty-dir Skip empty directories -n, --ignore Directories/files to not include diff --git a/src/HashUtil.Tests/HashTests.fs b/src/HashUtil.Tests/HashTests.fs index caa0a0e..b1111e6 100644 --- a/src/HashUtil.Tests/HashTests.fs +++ b/src/HashUtil.Tests/HashTests.fs @@ -20,7 +20,8 @@ type ChecksumTests(output: ITestOutputHelper) = "cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e" |] [| BLAKE3 "af1349b9f5f9a1a6a0404dea36dcc9499bcb25c9adc112b7cc9a93cae41f3262" |] - [| XXHASH3; "2d06800538d394c2" |] ] + [| XXHASH3; "2d06800538d394c2" |] + [| CRC32; "00000000" |] ] [] member _.``hash empty string``(hashType, expectedHash) = @@ -47,7 +48,8 @@ type ChecksumTests(output: ITestOutputHelper) = [| BLAKE3 "hello" "ea8f163db38682925e4491c5e58d4bb3506ef8c14eb78a86e908c5624a67200f" |] - [| XXHASH3; "hello"; "9555e8555c62dcfd" |] ] + [| XXHASH3; "hello"; "9555e8555c62dcfd" |] + [| CRC32; "hello"; "86a61036" |] ] [] @@ -67,6 +69,7 @@ type ChecksumTests(output: ITestOutputHelper) = [| [ "sha512"; " Sha512 "; "SHA512 " ]; Some SHA512 |] [| [ "blake3"; " Blake3 "; "BLAKE3 " ]; Some BLAKE3 |] [| [ "xxhash3"; " xxHash3 "; "XXHASH3 " ]; Some XXHASH3 |] + [| [ "crc32"; " crc32 "; "CRC32 " ]; Some CRC32 |] [| [ "asna1"; " md6 "; "SHA513 " ]; None |] ] [] diff --git a/src/HashUtil/Checksum.fs b/src/HashUtil/Checksum.fs index 8e12547..1edbb8c 100644 --- a/src/HashUtil/Checksum.fs +++ b/src/HashUtil/Checksum.fs @@ -17,6 +17,7 @@ module Checksum = | SHA512 | BLAKE3 | XXHASH3 + | CRC32 let allHashTypes: HashType[] = typeof @@ -36,6 +37,7 @@ module Checksum = | "SHA512" -> Some SHA512 | "BLAKE3" -> Some BLAKE3 | "XXHASH3" -> Some XXHASH3 + | "CRC32" -> Some CRC32 | _ -> None let getHashAlgorithm hashType : HashAlgorithm = @@ -47,4 +49,7 @@ module Checksum = | SHA384 -> SHA384.Create() | SHA512 -> SHA512.Create() | BLAKE3 -> new Blake3.Blake3HashAlgorithm() - | XXHASH3 -> new NonCryptoWrapper(new System.IO.Hashing.XxHash3()) + | XXHASH3 -> + new NonCryptoWrapper(new System.IO.Hashing.XxHash3() ) + | CRC32 -> + new NonCryptoWrapper(new System.IO.Hashing.Crc32() ) From b602cecd0c642e09fafe9b2685ce95ac8fb0441f Mon Sep 17 00:00:00 2001 From: Anu Bandi Date: Wed, 10 Dec 2025 23:32:38 -0800 Subject: [PATCH 4/6] change default to xxhash3 --- README.md | 20 ++++++++++---------- src/App/Program.fs | 2 +- src/HashUtil/Checksum.fs | 6 ++---- 3 files changed, 13 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index ac3edb1..df3a1bd 100644 --- a/README.md +++ b/README.md @@ -37,16 +37,16 @@ Arguments: Directory or file to hash/check Options: - -t, --tree Print directory tree - -s, --save Save the checksum to a file - -a, --algorithm The hash function to use [default: sha1] - -i, --include-hidden-files Include hidden files - -e, --skip-empty-dir Skip empty directories - -n, --ignore Directories/files to not include - -h, --hash-only Print only the hash - -c, --color Colorize the output [default: True] - --version Show version information - -?, -h, --help Show help and usage information + -t, --tree Print directory tree + -s, --save Save the checksum to a file + -a, --algorithm The hash function to use [default: xxhash3] + -i, --include-hidden-files Include hidden files + -e, --skip-empty-dir Skip empty directories + -n, --ignore Directories/files to not include + -h, --hash-only Print only the hash + -c, --color Colorize the output [default: True] + --version Show version information + -?, -h, --help Show help and usage information Commands: diff --git a/src/App/Program.fs b/src/App/Program.fs index 579bb4f..c1b6594 100644 --- a/src/App/Program.fs +++ b/src/App/Program.fs @@ -9,7 +9,7 @@ open System.CommandLine.Invocation open System.Threading -let defaultHashAlg = HashType.SHA1 +let defaultHashAlg = HashType.XXHASH3 type RootOpt ( diff --git a/src/HashUtil/Checksum.fs b/src/HashUtil/Checksum.fs index 1edbb8c..c67fc11 100644 --- a/src/HashUtil/Checksum.fs +++ b/src/HashUtil/Checksum.fs @@ -49,7 +49,5 @@ module Checksum = | SHA384 -> SHA384.Create() | SHA512 -> SHA512.Create() | BLAKE3 -> new Blake3.Blake3HashAlgorithm() - | XXHASH3 -> - new NonCryptoWrapper(new System.IO.Hashing.XxHash3() ) - | CRC32 -> - new NonCryptoWrapper(new System.IO.Hashing.Crc32() ) + | XXHASH3 -> new NonCryptoWrapper(new System.IO.Hashing.XxHash3()) + | CRC32 -> new NonCryptoWrapper(new System.IO.Hashing.Crc32()) From ee0dab289c4d9bf2911d8fda6bc320da91b1fe8d Mon Sep 17 00:00:00 2001 From: Anu Bandi Date: Mon, 15 Dec 2025 21:56:46 -0800 Subject: [PATCH 5/6] update agents --- AGENTS.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 0d8d1c3..51fcb9c 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -8,8 +8,8 @@ The tool supports many popular hashing algorithms such as `blake3`, `md5`, `ripe # How to Run -- **Dev Build:** `dotnet build` can be used to quickly make sure the app builds. -- **Run Tests:** `dotnet test` can be used to make sure the tests are passing. +- **Dev Build:** `make build` can be used to quickly make sure the app builds. +- **Run Tests:** `make test` can be used to make sure the tests are passing. - **Check changes/PR:** `git diff main` can be used to understand the feature branch. # Project Structure From c7f6be2bd5c3fac736b20d55763a6de1eb75a267 Mon Sep 17 00:00:00 2001 From: Anu Bandi Date: Mon, 15 Dec 2025 22:26:18 -0800 Subject: [PATCH 6/6] fix unit tests --- src/App.Tests/E2ETests.fs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/App.Tests/E2ETests.fs b/src/App.Tests/E2ETests.fs index b38541f..f864bcc 100644 --- a/src/App.Tests/E2ETests.fs +++ b/src/App.Tests/E2ETests.fs @@ -92,7 +92,7 @@ type FsTests debugOutput: ITestOutputHelper ) = let hashFile = - Path.Combine(fsTempDirSetupFixture.TempDir, "project_hash.sha1.txt") + Path.Combine(fsTempDirSetupFixture.TempDir, "project_hash.xxhash3.txt") let oldStdOut = Console.Out let customStdOut = new IO.StringWriter() @@ -365,7 +365,7 @@ type FsTests let getHashFilePath id = Path.Join( fsTempDirSetupFixture.TempDir, - sprintf "topA.txt.%s.sha1.txt" (id.ToString()) + sprintf "topA.txt.%s.xxhash3.txt" (id.ToString()) ) // Run hashdir multiple times.