diff --git a/pkg/utils/oshash.go b/pkg/utils/oshash.go index 75f552b84..aa7990d7c 100644 --- a/pkg/utils/oshash.go +++ b/pkg/utils/oshash.go @@ -1,37 +1,44 @@ package utils import ( - "bytes" "encoding/binary" + "errors" "fmt" "os" ) const chunkSize int64 = 64 * 1024 -func oshash(size int64, head []byte, tail []byte) (string, error) { - // put the head and tail together - buf := append(head, tail...) +var ErrOsHashLen = errors.New("buffer is not a multiple of 8") - // convert bytes into uint64 - ints := make([]uint64, len(buf)/8) - reader := bytes.NewReader(buf) - err := binary.Read(reader, binary.LittleEndian, &ints) - if err != nil { - return "", err +func sumBytes(buf []byte) (uint64, error) { + if len(buf)%8 != 0 { + return 0, ErrOsHashLen } - // sum the integers + sz := len(buf) / 8 var sum uint64 - for _, v := range ints { - sum += v + for j := 0; j < sz; j++ { + sum += binary.LittleEndian.Uint64(buf[8*j : 8*(j+1)]) } - // add the filesize - sum += uint64(size) + return sum, nil +} +func oshash(size int64, head []byte, tail []byte) (string, error) { + headSum, err := sumBytes(head) + if err != nil { + return "", fmt.Errorf("oshash head: %w", err) + } + tailSum, err := sumBytes(tail) + if err != nil { + return "", fmt.Errorf("oshash tail: %w", err) + } + + // Compute the sum of the head, tail and file size + result := headSum + tailSum + uint64(size) // output as hex - return fmt.Sprintf("%016x", sum), nil + return fmt.Sprintf("%016x", result), nil } // OSHashFromFilePath calculates the hash using the same algorithm that diff --git a/pkg/utils/oshash_internal_test.go b/pkg/utils/oshash_internal_test.go index b4d5b9d35..d9e709444 100644 --- a/pkg/utils/oshash_internal_test.go +++ b/pkg/utils/oshash_internal_test.go @@ -1,6 +1,7 @@ package utils import ( + "math/rand" "testing" ) @@ -44,3 +45,31 @@ func TestOshashCollisions(t *testing.T) { t.Errorf("TestOshashCollisions: oshash(n, k, ... %v) =! oshash(n, k, ... %v)", buf1, buf2) } } + +func BenchmarkOsHash(b *testing.B) { + src := rand.NewSource(9999) + r := rand.New(src) + + size := int64(1234567890) + + head := make([]byte, 1024*64) + _, err := r.Read(head) + if err != nil { + b.Errorf("unable to generate head array: %v", err) + } + + tail := make([]byte, 1024*64) + _, err = r.Read(tail) + if err != nil { + b.Errorf("unable to generate tail array: %v", err) + } + + b.ResetTimer() + + for n := 0; n < b.N; n++ { + _, err := oshash(size, head, tail) + if err != nil { + b.Errorf("unexpected error: %v", err) + } + } +}