diff --git a/go.mod b/go.mod index f7fa22e..da20cbc 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.24.0 require ( github.com/charmbracelet/bubbletea v1.3.4 github.com/charmbracelet/lipgloss v1.0.0 - golang.org/x/crypto v0.36.0 + github.com/zeebo/xxh3 v1.0.2 ) require ( @@ -13,6 +13,7 @@ require ( github.com/charmbracelet/x/ansi v0.8.0 // indirect github.com/charmbracelet/x/term v0.2.1 // indirect github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect + github.com/klauspost/cpuid/v2 v2.0.9 // indirect github.com/lucasb-eyer/go-colorful v1.2.0 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-localereader v0.0.1 // indirect diff --git a/go.sum b/go.sum index 54e585a..c821438 100644 --- a/go.sum +++ b/go.sum @@ -10,6 +10,8 @@ github.com/charmbracelet/x/term v0.2.1 h1:AQeHeLZ1OqSXhrAWpYUtZyX1T3zVxfpZuEQMIQ github.com/charmbracelet/x/term v0.2.1/go.mod h1:oQ4enTYFV7QN4m0i9mzHrViD7TQKvNEEkHUMCmsxdUg= github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f h1:Y/CXytFA4m6baUTXGLOoWe4PQhGxaX0KpnayAqC48p4= github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM= +github.com/klauspost/cpuid/v2 v2.0.9 h1:lgaqFMSdTdQYdZ04uHyN2d/eKdOMyi2YLSvlQIBFYa4= +github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= @@ -27,8 +29,10 @@ github.com/muesli/termenv v0.15.2/go.mod h1:Epx+iuz8sNs7mNKhxzH4fWXGNpZwUaJKRS1n github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= -golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34= -golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc= +github.com/zeebo/assert v1.3.0 h1:g7C04CbJuIDKNPFHmsk4hwZDO5O+kntRxzaUoNXj+IQ= +github.com/zeebo/assert v1.3.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0= +github.com/zeebo/xxh3 v1.0.2 h1:xZmwmqxHZA8AI603jOQ0tMqmBr9lPeFwGg6d+xy9DC0= +github.com/zeebo/xxh3 v1.0.2/go.mod h1:5NWz9Sef7zIDm2JHfFlcQvNekmcEl9ekUZQQKCYaDcA= golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw= golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= diff --git a/hash.go b/hash.go index 9f88f3a..7fe58f9 100644 --- a/hash.go +++ b/hash.go @@ -1,14 +1,25 @@ package main import ( - "encoding/hex" "fmt" "io" "os" + "sync" - "golang.org/x/crypto/blake2b" + "github.com/zeebo/xxh3" ) +const ( + bufferSize = 32 * 1024 +) + +var bufPool = sync.Pool{ + New: func() interface{} { + b := make([]byte, bufferSize) + return &b + }, +} + func createFileHash(filePath string) (string, error) { file, err := os.Open(filePath) if err != nil { @@ -16,14 +27,28 @@ func createFileHash(filePath string) (string, error) { } defer file.Close() - hash, err := blake2b.New256(nil) + var hash string + + hash, err = hashXXH3(file) + if err != nil { - return "", fmt.Errorf("failed to create BLAKE2b hash: %w", err) + return "", fmt.Errorf("hashing failed: %w", err) } - if _, err := io.Copy(hash, file); err != nil { - return "", fmt.Errorf("failed to compute hash: %w", err) + return hash, nil + +} + +func hashXXH3(r io.Reader) (string, error) { + hasher := xxh3.New() + buffPtr := bufPool.Get().(*[]byte) + buff := *buffPtr + defer bufPool.Put(buffPtr) + + if _, err := io.CopyBuffer(hasher, r, buff); err != nil { + return "", fmt.Errorf("XXH3 hashing failed: %w", err) } - return hex.EncodeToString(hash.Sum(nil)), nil + hash := hasher.Sum128() + return fmt.Sprintf("%016x%016x", hash.Hi, hash.Lo), nil } diff --git a/hash_test.go b/hash_test.go index 81c31f6..36e731c 100644 --- a/hash_test.go +++ b/hash_test.go @@ -2,12 +2,13 @@ package main import ( "encoding/hex" + "fmt" "io" "os" "path/filepath" "testing" - "golang.org/x/crypto/blake2b" + "github.com/zeebo/xxh3" ) func TestCreateFileHash(t *testing.T) { @@ -31,22 +32,22 @@ func TestCreateFileHash(t *testing.T) { } contentFile.Close() - h, err := blake2b.New256(nil) - if err != nil { - t.Fatalf("Failed to create BLAKE2b hash: %v", err) - } - n, err := io.WriteString(h, content) + hasher := xxh3.New() + + n, err := io.WriteString(hasher, content) if err != nil { t.Error(err) } if n != len(content) { t.Errorf("not all bytes are written, expected to write %d bytes, written: %d", len(content), n) } - expectedContentHash := hex.EncodeToString(h.Sum(nil)) - emptyHash, err := blake2b.New256(nil) + hash := hasher.Sum128() + expectedContentHash := fmt.Sprintf("%016x%016x", hash.Hi, hash.Lo) + + emptyHash := xxh3.New() if err != nil { - t.Fatalf("Failed to create BLAKE2b hash for empty file: %v", err) + t.Fatalf("Failed to create XXH3 hash for empty file: %v", err) } expectedEmptyHash := hex.EncodeToString(emptyHash.Sum(nil))