diff --git a/lib/scanner/blocks.go b/lib/scanner/blocks.go index a67ac9208..6e2cc6743 100644 --- a/lib/scanner/blocks.go +++ b/lib/scanner/blocks.go @@ -108,26 +108,19 @@ func Blocks(ctx context.Context, r io.Reader, blocksize int, sizehint int64, cou return blocks, nil } +// Validate quickly validates buf against the cryptohash hash (if len(hash)>0) +// and the 32-bit hash weakHash (if not zero). It is satisfied if either hash +// matches, or neither is given. func Validate(buf, hash []byte, weakHash uint32) bool { - rd := bytes.NewReader(buf) if weakHash != 0 { - whf := adler32.New() - if _, err := io.Copy(whf, rd); err == nil && whf.Sum32() == weakHash { - return true - } - // Copy error or mismatch, go to next algo. - rd.Seek(0, io.SeekStart) + return adler32.Checksum(buf) == weakHash } if len(hash) > 0 { - hf := sha256.New() - if _, err := io.Copy(hf, rd); err == nil { - // Sum allocates, so let's hope we don't hit this often. - return bytes.Equal(hf.Sum(nil), hash) - } + hbuf := sha256.Sum256(buf) + return bytes.Equal(hbuf[:], hash) } - // Both algos failed or no hashes were specified. Assume it's all good. return true } diff --git a/lib/scanner/blocks_test.go b/lib/scanner/blocks_test.go index 99f1a7164..011a4f97e 100644 --- a/lib/scanner/blocks_test.go +++ b/lib/scanner/blocks_test.go @@ -12,11 +12,13 @@ import ( "crypto/rand" "fmt" origAdler32 "hash/adler32" + mrand "math/rand" "testing" "testing/quick" rollingAdler32 "github.com/chmduquesne/rollinghash/adler32" "github.com/syncthing/syncthing/lib/protocol" + "github.com/syncthing/syncthing/lib/sha256" ) var blocksTestData = []struct { @@ -165,3 +167,43 @@ func TestAdler32Variants(t *testing.T) { hf3.Roll(data[i]) } } + +func BenchmarkValidate(b *testing.B) { + type block struct { + data []byte + hash [sha256.Size]byte + weakhash uint32 + } + var blocks []block + const blocksPerType = 100 + + r := mrand.New(mrand.NewSource(0x136bea689e851)) + + // Valid blocks. + for i := 0; i < blocksPerType; i++ { + var b block + b.data = make([]byte, 128<<10) + r.Read(b.data[:]) + b.hash = sha256.Sum256(b.data[:]) + b.weakhash = origAdler32.Checksum(b.data[:]) + blocks = append(blocks, b) + } + // Blocks where the hash matches, but the weakhash doesn't. + for i := 0; i < blocksPerType; i++ { + var b block + b.data = make([]byte, 128<<10) + r.Read(b.data[:]) + b.hash = sha256.Sum256(b.data[:]) + b.weakhash = 1 // Zeros causes Validate to skip the weakhash. + blocks = append(blocks, b) + } + + b.ReportAllocs() + b.ResetTimer() + + for i := 0; i < b.N; i++ { + for _, b := range blocks { + Validate(b.data[:], b.hash[:], b.weakhash) + } + } +}