201 lines
4.8 KiB
Diff
201 lines
4.8 KiB
Diff
|
|
From a1becee58561451daea4bd69989b0806cfa9ceab Mon Sep 17 00:00:00 2001
|
||
|
|
From: DCCooper <1866858@gmail.com>
|
||
|
|
Date: Tue, 16 Nov 2021 17:31:58 +0800
|
||
|
|
Subject: [PATCH 22/29] perf:use bufio reader instead ioutil.ReadFile
|
||
|
|
|
||
|
|
reason: read file with fixed chunk size instead of
|
||
|
|
the whole file into memory cause memory pressure
|
||
|
|
|
||
|
|
Signed-off-by: DCCooper <1866858@gmail.com>
|
||
|
|
---
|
||
|
|
util/cipher.go | 38 +++++++++++++++++++-----
|
||
|
|
util/cipher_test.go | 84 +++++++++++++++++++++++++++++++++++++++++++++++++++++
|
||
|
|
2 files changed, 114 insertions(+), 8 deletions(-)
|
||
|
|
|
||
|
|
diff --git a/util/cipher.go b/util/cipher.go
|
||
|
|
index ecbbc47..a5e3125 100644
|
||
|
|
--- a/util/cipher.go
|
||
|
|
+++ b/util/cipher.go
|
||
|
|
@@ -14,6 +14,7 @@
|
||
|
|
package util
|
||
|
|
|
||
|
|
import (
|
||
|
|
+ "bufio"
|
||
|
|
"crypto"
|
||
|
|
"crypto/aes"
|
||
|
|
"crypto/cipher"
|
||
|
|
@@ -234,6 +235,33 @@ func ReadPublicKey(path string) (rsa.PublicKey, error) {
|
||
|
|
return *key, nil
|
||
|
|
}
|
||
|
|
|
||
|
|
+func checkSumReader(path string) (string, error) {
|
||
|
|
+ const bufferSize = 32 * 1024 // 32KB
|
||
|
|
+
|
||
|
|
+ file, err := os.Open(filepath.Clean(path))
|
||
|
|
+ if err != nil {
|
||
|
|
+ return "", errors.Wrapf(err, "hash file failed")
|
||
|
|
+ }
|
||
|
|
+ defer func() {
|
||
|
|
+ if cErr := file.Close(); cErr != nil && err == nil {
|
||
|
|
+ err = cErr
|
||
|
|
+ }
|
||
|
|
+ }()
|
||
|
|
+ buf := make([]byte, bufferSize)
|
||
|
|
+ reader := bufio.NewReader(file)
|
||
|
|
+ hasher := sha256.New()
|
||
|
|
+ for {
|
||
|
|
+ switch n, err := reader.Read(buf); err {
|
||
|
|
+ case nil:
|
||
|
|
+ hasher.Write(buf[:n])
|
||
|
|
+ case io.EOF:
|
||
|
|
+ return fmt.Sprintf("%x", hasher.Sum(nil)), nil
|
||
|
|
+ default:
|
||
|
|
+ return "", err
|
||
|
|
+ }
|
||
|
|
+ }
|
||
|
|
+}
|
||
|
|
+
|
||
|
|
func hashFile(path string) (string, error) {
|
||
|
|
cleanPath := filepath.Clean(path)
|
||
|
|
if f, err := os.Stat(cleanPath); err != nil {
|
||
|
|
@@ -242,12 +270,7 @@ func hashFile(path string) (string, error) {
|
||
|
|
return "", errors.New("failed to hash directory")
|
||
|
|
}
|
||
|
|
|
||
|
|
- file, err := ioutil.ReadFile(cleanPath) // nolint:gosec
|
||
|
|
- if err != nil {
|
||
|
|
- return "", errors.Wrapf(err, "hash file failed")
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- return fmt.Sprintf("%x", sha256.Sum256(file)), nil
|
||
|
|
+ return checkSumReader(path)
|
||
|
|
}
|
||
|
|
|
||
|
|
func hashDir(path string) (string, error) {
|
||
|
|
@@ -261,11 +284,10 @@ func hashDir(path string) (string, error) {
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
if !info.IsDir() {
|
||
|
|
- f, err := ioutil.ReadFile(cleanPath) // nolint:gosec
|
||
|
|
+ fileHash, err := hashFile(cleanPath)
|
||
|
|
if err != nil {
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
- fileHash := fmt.Sprintf("%x", sha256.Sum256(f))
|
||
|
|
checkSum = fmt.Sprintf("%s%s", checkSum, fileHash)
|
||
|
|
}
|
||
|
|
return nil
|
||
|
|
diff --git a/util/cipher_test.go b/util/cipher_test.go
|
||
|
|
index bab6dfe..4bbe894 100644
|
||
|
|
--- a/util/cipher_test.go
|
||
|
|
+++ b/util/cipher_test.go
|
||
|
|
@@ -15,10 +15,13 @@ package util
|
||
|
|
|
||
|
|
import (
|
||
|
|
"crypto"
|
||
|
|
+ "crypto/rand"
|
||
|
|
"crypto/sha1"
|
||
|
|
"crypto/sha256"
|
||
|
|
"crypto/sha512"
|
||
|
|
+ "fmt"
|
||
|
|
"hash"
|
||
|
|
+ "io"
|
||
|
|
"io/ioutil"
|
||
|
|
"os"
|
||
|
|
"path/filepath"
|
||
|
|
@@ -31,6 +34,9 @@ import (
|
||
|
|
)
|
||
|
|
|
||
|
|
const (
|
||
|
|
+ sizeKB = 1024
|
||
|
|
+ sizeMB = 1024 * sizeKB
|
||
|
|
+ sizeGB = 1024 * sizeMB
|
||
|
|
maxRepeatTime = 1000000
|
||
|
|
)
|
||
|
|
|
||
|
|
@@ -453,3 +459,81 @@ func TestCheckSum(t *testing.T) {
|
||
|
|
})
|
||
|
|
}
|
||
|
|
}
|
||
|
|
+
|
||
|
|
+func createFileWithSize(path string, size int) error {
|
||
|
|
+ file, err := os.Create(path)
|
||
|
|
+ if err != nil {
|
||
|
|
+ return err
|
||
|
|
+ }
|
||
|
|
+ _, err = io.CopyN(file, rand.Reader, int64(size))
|
||
|
|
+ return err
|
||
|
|
+}
|
||
|
|
+
|
||
|
|
+func benchmarkSHA256SumWithFileSize(b *testing.B, fileSize int) {
|
||
|
|
+ b.ReportAllocs()
|
||
|
|
+ filepath := fs.NewFile(b, b.Name())
|
||
|
|
+ defer filepath.Remove()
|
||
|
|
+ _ = createFileWithSize(filepath.Path(), fileSize)
|
||
|
|
+ b.ResetTimer()
|
||
|
|
+ for n := 0; n < b.N; n++ {
|
||
|
|
+ _, _ = SHA256Sum(filepath.Path())
|
||
|
|
+ }
|
||
|
|
+}
|
||
|
|
+
|
||
|
|
+func BenchmarkSHA256Sum(b *testing.B) {
|
||
|
|
+ tests := []struct {
|
||
|
|
+ fileSuffix string
|
||
|
|
+ fileSize int
|
||
|
|
+ }{
|
||
|
|
+ {fileSuffix: "100MB", fileSize: 100 * sizeMB},
|
||
|
|
+ {fileSuffix: "200MB", fileSize: 200 * sizeMB},
|
||
|
|
+ {fileSuffix: "500MB", fileSize: 500 * sizeMB},
|
||
|
|
+ {fileSuffix: "1GB", fileSize: 1 * sizeGB},
|
||
|
|
+ {fileSuffix: "2GB", fileSize: 2 * sizeGB},
|
||
|
|
+ {fileSuffix: "4GB", fileSize: 4 * sizeGB},
|
||
|
|
+ {fileSuffix: "8GB", fileSize: 8 * sizeGB},
|
||
|
|
+ }
|
||
|
|
+
|
||
|
|
+ for _, t := range tests {
|
||
|
|
+ name := fmt.Sprintf("BenchmarkSHA256SumWithFileSize_%s", t.fileSuffix)
|
||
|
|
+ b.Run(name, func(b *testing.B) {
|
||
|
|
+ benchmarkSHA256SumWithFileSize(b, t.fileSize)
|
||
|
|
+ })
|
||
|
|
+ }
|
||
|
|
+}
|
||
|
|
+
|
||
|
|
+func TestCreateFileWithSize(t *testing.T) {
|
||
|
|
+ newFile := fs.NewFile(t, t.Name())
|
||
|
|
+ defer newFile.Remove()
|
||
|
|
+ type args struct {
|
||
|
|
+ path string
|
||
|
|
+ size int
|
||
|
|
+ }
|
||
|
|
+ tests := []struct {
|
||
|
|
+ name string
|
||
|
|
+ args args
|
||
|
|
+ wantErr bool
|
||
|
|
+ }{
|
||
|
|
+ {
|
||
|
|
+ name: "TC-generate 500MB file",
|
||
|
|
+ args: args{
|
||
|
|
+ path: newFile.Path(),
|
||
|
|
+ size: 500 * sizeMB,
|
||
|
|
+ },
|
||
|
|
+ },
|
||
|
|
+ }
|
||
|
|
+ for _, tt := range tests {
|
||
|
|
+ t.Run(tt.name, func(t *testing.T) {
|
||
|
|
+ err := createFileWithSize(tt.args.path, tt.args.size)
|
||
|
|
+ if (err != nil) != tt.wantErr {
|
||
|
|
+ t.Errorf("createFileWithSize() error = %v, wantErr %v", err, tt.wantErr)
|
||
|
|
+ }
|
||
|
|
+ if err == nil {
|
||
|
|
+ file, _ := os.Stat(tt.args.path)
|
||
|
|
+ if file.Size() != int64(tt.args.size) {
|
||
|
|
+ t.Errorf("createFileWithSize() size = %v, actually %v", tt.args.size, file.Size())
|
||
|
|
+ }
|
||
|
|
+ }
|
||
|
|
+ })
|
||
|
|
+ }
|
||
|
|
+}
|
||
|
|
--
|
||
|
|
1.8.3.1
|
||
|
|
|