diff --git a/README.md b/README.md index 2899ee1..bb411f3 100644 --- a/README.md +++ b/README.md @@ -50,7 +50,8 @@ You can find interactive examples of using it as a package to [encrypt](https:// ### Performance -With the default settings the encryption should be about **18.5** times faster than `gpgtar` and **8.8** times faster than `7zip` +- Encryption should be about **20.2** times faster than `gpgtar`, and the decryption **3.3** times. +- Encryption should be about **9.1** times faster than `7zip`, and the decryption **9.5** times. > [!NOTE] > You can reproduce the results by running [bench_and_plot.py](benchmark/bench_and_plot.py) (based on [Matplotlib](https://github.com/matplotlib/matplotlib) and [Hyperfine](https://github.com/sharkdp/hyperfine)) diff --git a/benchmark/decryption-time.webp b/benchmark/decryption-time.webp index bcfceab..8aedb9a 100644 Binary files a/benchmark/decryption-time.webp and b/benchmark/decryption-time.webp differ diff --git a/benchmark/decryption.json b/benchmark/decryption.json index 76f4041..c808168 100644 --- a/benchmark/decryption.json +++ b/benchmark/decryption.json @@ -2,17 +2,17 @@ "results": [ { "command": "echo \"123456789\" | ~/Projects/safelock-cli/safelock-cli decrypt test.sla safelock_dump --quiet", - "mean": 2.1050263989333335, - "stddev": 0.06409584697239645, - "median": 2.1340488396, - "user": 2.565836426666667, - "system": 1.86455084, - "min": 2.0315528936000002, - "max": 2.1494774636000002, + "mean": 2.1295176539933336, + "stddev": 0.03361454776096437, + "median": 2.13093239266, + "user": 2.5876522466666665, + "system": 1.88427306, + "min": 2.0952180726600003, + "max": 2.16240249666, "times": [ - 2.1494774636000002, - 2.1340488396, - 2.0315528936000002 + 2.16240249666, + 2.13093239266, + 2.0952180726600003 ], "exit_codes": [ 0, @@ -22,17 +22,17 @@ }, { "command": "echo \"123456789\" | ~/Projects/safelock-cli/safelock-cli decrypt test_sha256.sla safelock_dump --quiet --sha256", - "mean": 1.7911251366, - "stddev": 0.21521963683077622, - "median": 1.7543641936, - "user": 1.9575960933333334, - "system": 1.67062184, - "min": 1.5966536246, - "max": 2.0223575916, + "mean": 1.8789916449933333, + "stddev": 0.17816416646803604, + "median": 1.98184108566, + "user": 2.0644982466666666, + "system": 1.776121393333333, + "min": 1.67326538666, + "max": 1.9818684626599998, "times": [ - 1.7543641936, - 2.0223575916, - 1.5966536246 + 1.67326538666, + 1.98184108566, + 1.9818684626599998 ], "exit_codes": [ 0, @@ -42,17 +42,17 @@ }, { "command": "echo \"123456789\" | ~/Projects/safelock-cli/safelock-cli decrypt test_sha512.sla safelock_dump --quiet --sha512", - "mean": 2.268577597266667, - "stddev": 0.026547606953880087, - "median": 2.2732154626, - "user": 2.8112754266666666, - "system": 1.9018115066666663, - "min": 2.2400166546, - "max": 2.2925006746000003, + "mean": 2.123450407993334, + "stddev": 0.1763774710740607, + "median": 2.1136010126600002, + "user": 2.5975039133333335, + "system": 1.7468127266666666, + "min": 1.95220401166, + "max": 2.3045461996600003, "times": [ - 2.2732154626, - 2.2400166546, - 2.2925006746000003 + 2.3045461996600003, + 2.1136010126600002, + 1.95220401166 ], "exit_codes": [ 0, @@ -62,17 +62,17 @@ }, { "command": "7z e -y -p123456789 -mx1 test.7z -osafelock_dump", - "mean": 18.642169893266665, - "stddev": 1.1840904020013951, - "median": 17.9893611436, - "user": 20.570662093333336, - "system": 1.4070678399999998, - "min": 17.9281653906, - "max": 20.0089831456, + "mean": 17.944166026326666, + "stddev": 0.03535223844853417, + "median": 17.95858265166, + "user": 19.665792913333334, + "system": 1.4092493933333332, + "min": 17.90388353266, + "max": 17.97003189466, "times": [ - 20.0089831456, - 17.9281653906, - 17.9893611436 + 17.97003189466, + 17.90388353266, + 17.95858265166 ], "exit_codes": [ 0, @@ -82,17 +82,17 @@ }, { "command": "gpgtar -d --yes --batch --gpg-args \"--passphrase 123456789\" test.gpg", - "mean": 5.750362993933334, - "stddev": 0.2899378904750308, - "median": 5.6110951406, - "user": 0.1864430933333333, - "system": 1.3356181733333334, - "min": 5.5563355296, - "max": 6.0836583116, + "mean": 6.240754918993335, + "stddev": 0.18841334623779463, + "median": 6.18021860166, + "user": 0.17699391333333328, + "system": 1.4514090599999998, + "min": 6.09005041466, + "max": 6.45199574066, "times": [ - 6.0836583116, - 5.6110951406, - 5.5563355296 + 6.45199574066, + 6.18021860166, + 6.09005041466 ], "exit_codes": [ 0, diff --git a/benchmark/encryption-time.webp b/benchmark/encryption-time.webp index 6b09361..98b5e14 100644 Binary files a/benchmark/encryption-time.webp and b/benchmark/encryption-time.webp differ diff --git a/benchmark/encryption.json b/benchmark/encryption.json index c345a14..f268dff 100644 --- a/benchmark/encryption.json +++ b/benchmark/encryption.json @@ -2,17 +2,17 @@ "results": [ { "command": "echo \"123456789\" | ~/Projects/safelock-cli/safelock-cli encrypt Videos test.sla --quiet", - "mean": 2.307632567606667, - "stddev": 0.2723097797445358, - "median": 2.31758881894, - "user": 3.316105306666667, - "system": 1.43377396, - "min": 2.03048120494, - "max": 2.57482767894, + "mean": 2.2742533827266667, + "stddev": 0.05119318397940522, + "median": 2.28937216306, + "user": 3.329233973333333, + "system": 1.3630514999999999, + "min": 2.21720349906, + "max": 2.31618448606, "times": [ - 2.57482767894, - 2.03048120494, - 2.31758881894 + 2.21720349906, + 2.31618448606, + 2.28937216306 ], "exit_codes": [ 0, @@ -22,17 +22,17 @@ }, { "command": "echo \"123456789\" | ~/Projects/safelock-cli/safelock-cli encrypt Videos test_sha256.sla --quiet --sha256", - "mean": 2.3091812516066668, - "stddev": 0.34126317439508236, - "median": 2.23056378494, - "user": 3.660151306666666, - "system": 1.4299016266666664, - "min": 2.01408749594, - "max": 2.68289247394, + "mean": 2.2029850927266668, + "stddev": 0.2887786765513728, + "median": 2.29156615106, + "user": 3.45133264, + "system": 1.3318238333333332, + "min": 1.88029167006, + "max": 2.43709745706, "times": [ - 2.68289247394, - 2.23056378494, - 2.01408749594 + 2.43709745706, + 1.88029167006, + 2.29156615106 ], "exit_codes": [ 0, @@ -42,17 +42,17 @@ }, { "command": "echo \"123456789\" | ~/Projects/safelock-cli/safelock-cli encrypt Videos test_sha512.sla --quiet --sha512", - "mean": 2.2512121786066666, - "stddev": 0.4150328365179191, - "median": 2.27823183994, - "user": 3.4754706400000006, - "system": 1.4172956266666665, - "min": 1.82332967894, - "max": 2.6520750169400005, + "mean": 2.3804781427266666, + "stddev": 0.2607000393652314, + "median": 2.29337213106, + "user": 3.7844909733333325, + "system": 1.4182584999999996, + "min": 2.17448372306, + "max": 2.67357857406, "times": [ - 2.6520750169400005, - 2.27823183994, - 1.82332967894 + 2.67357857406, + 2.17448372306, + 2.29337213106 ], "exit_codes": [ 0, @@ -62,17 +62,17 @@ }, { "command": "7z a -p123456789 -mx1 test.7z Videos", - "mean": 20.219332223273334, - "stddev": 0.21725406948835344, - "median": 20.212478717939998, - "user": 138.90269830666668, - "system": 0.97966096, - "min": 20.00558599694, - "max": 20.43993195494, + "mean": 20.227981493393333, + "stddev": 0.47311415510031346, + "median": 20.17403875506, + "user": 137.52122964, + "system": 0.9545411666666667, + "min": 19.78415073906, + "max": 20.72575498606, "times": [ - 20.00558599694, - 20.212478717939998, - 20.43993195494 + 20.72575498606, + 20.17403875506, + 19.78415073906 ], "exit_codes": [ 0, @@ -82,17 +82,17 @@ }, { "command": "gpgtar -e -o test.gpg -c --yes --batch --gpg-args \"--passphrase 123456789\" Videos", - "mean": 42.574974254606666, - "stddev": 0.8429570446257044, - "median": 42.49198231794, - "user": 32.93103530666667, - "system": 9.175310626666667, - "min": 41.77658282994, - "max": 43.45635761594, + "mean": 44.902245365726664, + "stddev": 1.3460233818310492, + "median": 44.85683033906, + "user": 33.18701430666666, + "system": 9.421267166666667, + "min": 43.57950423606, + "max": 46.27040152206, "times": [ - 41.77658282994, - 42.49198231794, - 43.45635761594 + 46.27040152206, + 43.57950423606, + 44.85683033906 ], "exit_codes": [ 0, diff --git a/benchmark/file-size.webp b/benchmark/file-size.webp index e710d6a..619c669 100644 Binary files a/benchmark/file-size.webp and b/benchmark/file-size.webp differ diff --git a/cmd/root.go b/cmd/root.go index d604b54..96551a1 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -16,7 +16,7 @@ var rootCmd = &cobra.Command{ Use: "safelock-cli", Short: "Simple tool to encrypt/decrypt files with AES encryption", Long: "Simple command-line tool to encrypt and decrypt files with AES encryption", - Version: "0.4.2", + Version: "0.4.3", CompletionOptions: cobra.CompletionOptions{ DisableDefaultCmd: true, }, diff --git a/safelock/core.go b/safelock/core.go index 16c36cb..1369669 100644 --- a/safelock/core.go +++ b/safelock/core.go @@ -24,8 +24,8 @@ type asyncGcm struct { done chan bool } -func newAsyncGcm(pwd string, config EncryptionConfig, errs chan<- error) *asyncGcm { - ag := &asyncGcm{ +func newAsyncGcm(pwd string, config EncryptionConfig, errs chan<- error) asyncGcm { + ag := asyncGcm{ pwd: pwd, config: config, errs: errs, diff --git a/safelock/decrypt.go b/safelock/decrypt.go index 84af0cd..7f79eaf 100644 --- a/safelock/decrypt.go +++ b/safelock/decrypt.go @@ -16,9 +16,9 @@ import ( // and then outputs the content into `outputPath` which must be a valid path to an existing directory // // NOTE: `ctx` context is optional you can pass `nil` and the method will handle it -func (sl *Safelock) Decrypt(ctx context.Context, input InputReader, outputPath, password string) (err error) { +func (sl Safelock) Decrypt(ctx context.Context, input InputReader, outputPath, password string) (err error) { errs := make(chan error) - signals, closeSignals := sl.getExitSignals() + signals, closeSignals := utils.GetExitSignals() if ctx == nil { ctx = context.Background() @@ -33,27 +33,25 @@ func (sl *Safelock) Decrypt(ctx context.Context, input InputReader, outputPath, Trigger(StatusEnd.Str()) go func() { - var calc *utils.PercentCalculator - if err = sl.validateDecryptionPaths(outputPath); err != nil { errs <- fmt.Errorf("invalid decryption input > %w", err) return } - if calc, err = utils.NewSeekerCalculator(input, 1.0); err != nil { - errs <- fmt.Errorf("failed to read input paths > %w", err) + ctx, cancel := context.WithCancel(ctx) + reader := newReader(password, input, 1.0, cancel, sl.EncryptionConfig, errs) + + if err = reader.setInputSize(); err != nil { + errs <- fmt.Errorf("failed to read input > %w", err) return } - ctx, cancel := context.WithCancel(ctx) - rw := newReader(password, input, cancel, calc, sl.EncryptionConfig, errs) - - if err = rw.ReadHeader(); err != nil { - errs <- fmt.Errorf("failed to read input file header > %w", err) + if err = reader.ReadHeader(); err != nil { + errs <- fmt.Errorf("failed to read input header > %w", err) return } - if err = sl.decryptFiles(ctx, outputPath, rw, calc); err != nil { + if err = sl.decryptFiles(ctx, outputPath, reader); err != nil { errs <- fmt.Errorf("failed to extract archive file > %w", err) return } @@ -87,19 +85,18 @@ func (sl *Safelock) validateDecryptionPaths(outputPath string) (err error) { return } -func (sl *Safelock) decryptFiles( +func (sl Safelock) decryptFiles( ctx context.Context, outputPath string, - slReader *safelockReader, - calc *utils.PercentCalculator, + slReader safelockReader, ) (err error) { var reader io.ReadCloser - if reader, err = sl.Compression.OpenReader(slReader); err != nil { + if reader, err = sl.Compression.OpenReader(&slReader); err != nil { return fmt.Errorf("cannot read archive file > %w", err) } - go sl.updateProgressStatus(ctx, "Decrypting", calc) + go sl.updateProgressStatus(ctx, "Decrypting", slReader) fileHandler := getExtractFileHandler(outputPath) diff --git a/safelock/encrypt.go b/safelock/encrypt.go index fbe5eaa..d10b31e 100644 --- a/safelock/encrypt.go +++ b/safelock/encrypt.go @@ -5,8 +5,6 @@ import ( "fmt" "io" "os" - "os/signal" - "syscall" "time" "github.com/mholt/archiver/v4" @@ -18,9 +16,9 @@ import ( // outputs into an object `output` that implements [io.Writer] such as [io.File] // // NOTE: `ctx` context is optional you can pass `nil` and the method will handle it -func (sl *Safelock) Encrypt(ctx context.Context, inputPaths []string, output io.Writer, password string) (err error) { +func (sl Safelock) Encrypt(ctx context.Context, inputPaths []string, output io.Writer, password string) (err error) { errs := make(chan error) - signals, closeSignals := sl.getExitSignals() + signals, closeSignals := utils.GetExitSignals() if ctx == nil { ctx = context.Background() @@ -41,16 +39,16 @@ func (sl *Safelock) Encrypt(ctx context.Context, inputPaths []string, output io. } ctx, cancel := context.WithCancel(ctx) - calc := utils.NewPathsCalculator(20.0) - rw := newWriter(password, output, cancel, calc, sl.EncryptionConfig, errs) - rw.asyncGcm = newAsyncGcm(password, sl.EncryptionConfig, errs) + config := sl.EncryptionConfig + gcm := newAsyncGcm(password, config, errs) + writer := newWriter(password, output, 20.0, cancel, gcm, config, errs) - if err = sl.encryptFiles(ctx, inputPaths, rw, calc); err != nil { + if err = sl.encryptFiles(ctx, inputPaths, writer); err != nil { errs <- err return } - if err = rw.WriteHeader(); err != nil { + if err = writer.WriteHeader(); err != nil { errs <- fmt.Errorf("failed to create encrypted file header > %w", err) return } @@ -74,18 +72,7 @@ func (sl *Safelock) Encrypt(ctx context.Context, inputPaths []string, output io. } } -func (sl *Safelock) getExitSignals() (<-chan os.Signal, func()) { - signals := make(chan os.Signal, 2) - close := func() { - signal.Stop(signals) - close(signals) - } - - signal.Notify(signals, syscall.SIGTERM, syscall.SIGINT) - return signals, close -} - -func (sl *Safelock) validateEncryptionInputs(inputPaths []string, pwd string) (err error) { +func (sl Safelock) validateEncryptionInputs(inputPaths []string, pwd string) (err error) { sl.updateStatus("Validating inputs", 0.0) for _, path := range inputPaths { @@ -101,15 +88,14 @@ func (sl *Safelock) validateEncryptionInputs(inputPaths []string, pwd string) (e return } -func (sl *Safelock) encryptFiles( +func (sl Safelock) encryptFiles( ctx context.Context, inputPaths []string, - slWriter *safelockWriter, - calc *utils.PercentCalculator, + slWriter safelockWriter, ) (err error) { var files []archiver.File var filesMap = make(map[string]string, len(inputPaths)) - var cancelListingStatus = sl.updateListingStatus(ctx, 1.0, calc.Start) + var cancelListingStatus = sl.updateListingStatus(ctx, 1.0, slWriter.start) for _, path := range inputPaths { filesMap[path] = "" @@ -122,14 +108,14 @@ func (sl *Safelock) encryptFiles( go func() { for _, file := range files { - calc.InputSize += int(file.Size()) + slWriter.increaseInputSize(int(file.Size())) } cancelListingStatus() - sl.updateProgressStatus(ctx, "Encrypting", calc) + sl.updateProgressStatus(ctx, "Encrypting", slWriter) }() - if err = sl.archive(ctx, slWriter, files); err != nil { + if err = sl.archive(ctx, &slWriter, files); err != nil { err = fmt.Errorf("failed to create encrypted archive file > %w", err) return } @@ -138,7 +124,7 @@ func (sl *Safelock) encryptFiles( return } -func (sl *Safelock) updateListingStatus(ctx context.Context, start, end float64) (cancel context.CancelFunc) { +func (sl Safelock) updateListingStatus(ctx context.Context, start, end float64) (cancel context.CancelFunc) { ctx, cancel = context.WithCancel(ctx) go func() { @@ -161,13 +147,13 @@ func (sl *Safelock) updateListingStatus(ctx context.Context, start, end float64) return } -func (sl *Safelock) updateProgressStatus(ctx context.Context, act string, calc *utils.PercentCalculator) { +func (sl Safelock) updateProgressStatus(ctx context.Context, act string, rw getPercent) { for { select { case <-ctx.Done(): return default: - sl.updateStatus(fmt.Sprintf("%s files", act), calc.GetPercent()) + sl.updateStatus(fmt.Sprintf("%s files", act), rw.getCompletedPercent()) time.Sleep(time.Second / 5) } } diff --git a/safelock/reader.go b/safelock/reader.go index d360554..5e55e72 100644 --- a/safelock/reader.go +++ b/safelock/reader.go @@ -9,7 +9,6 @@ import ( "strings" "github.com/mrf345/safelock-cli/slErrs" - "github.com/mrf345/safelock-cli/utils" ) type InputReader interface { @@ -19,7 +18,7 @@ type InputReader interface { type safelockReader struct { io.Reader - safelockReaderWriterBase + *safelockReaderWriterBase reader InputReader overflow []byte } @@ -27,29 +26,36 @@ type safelockReader struct { func newReader( pwd string, reader InputReader, + start float64, cancel context.CancelFunc, - calc *utils.PercentCalculator, config EncryptionConfig, errs chan<- error, -) *safelockReader { - return &safelockReader{ +) safelockReader { + return safelockReader{ reader: reader, - safelockReaderWriterBase: safelockReaderWriterBase{ + safelockReaderWriterBase: &safelockReaderWriterBase{ pwd: pwd, - calc: calc, errs: errs, cancel: cancel, config: config, + start: start, + end: 100.0, }, } } -func (sr *safelockReader) ReadHeader() (err error) { - sr.setSize() +func (sr *safelockReader) setInputSize() (err error) { + size, err := sr.reader.Seek(0, io.SeekEnd) + sr.inputSize = int(size) + return +} - headerBytes := make([]byte, sr.headerSize) +func (sr *safelockReader) ReadHeader() (err error) { + headerSize := sr.config.getHeaderSizeOut(sr.inputSize) + sizeDiff := int64(sr.inputSize - headerSize) + headerBytes := make([]byte, headerSize) - if _, err = sr.reader.Seek(sr.diffSize(), io.SeekStart); err != nil { + if _, err = sr.reader.Seek(sizeDiff, io.SeekStart); err != nil { err = fmt.Errorf("can't seek header > %w", err) return sr.handleErr(err) } @@ -74,11 +80,6 @@ func (sr *safelockReader) ReadHeader() (err error) { return } -func (sr *safelockReader) setSize() { - sr.size = sr.calc.InputSize - sr.headerSize = sr.config.getHeaderSizeOut(sr.size) -} - func (sr *safelockReader) Read(chunk []byte) (read int, err error) { var blockSize int var block string @@ -115,7 +116,7 @@ func (sr *safelockReader) Read(chunk []byte) (read int, err error) { return read, sr.handleErr(err) } - sr.calc.OutputSize += len(decrypted) + sr.outputSize += len(decrypted) return sr.handleOverflowOut(&chunk, decrypted), nil } diff --git a/safelock/readerWriter.go b/safelock/readerWriter.go index b49ed12..7482e5b 100644 --- a/safelock/readerWriter.go +++ b/safelock/readerWriter.go @@ -2,25 +2,21 @@ package safelock import ( "context" - - "github.com/mrf345/safelock-cli/utils" ) -type safelockReaderWriterBase struct { - config EncryptionConfig - pwd string - errs chan<- error - cancel context.CancelFunc - calc *utils.PercentCalculator - - size int - headerSize int - blocks []string - err error +type getPercent interface { + getCompletedPercent() float64 } -func (srw *safelockReaderWriterBase) diffSize() int64 { - return int64(srw.size - srw.headerSize) +type safelockReaderWriterBase struct { + config EncryptionConfig + pwd string + errs chan<- error + cancel context.CancelFunc + blocks []string + err error + start, end float64 + inputSize, outputSize int } func (srw *safelockReaderWriterBase) handleErr(err error) error { @@ -29,3 +25,17 @@ func (srw *safelockReaderWriterBase) handleErr(err error) error { srw.cancel() return err } + +func (srw safelockReaderWriterBase) getCompletedPercent() float64 { + percent := srw.start + (float64(srw.outputSize) / float64(srw.inputSize) * srw.end) + + if srw.end > percent { + return percent + } + + return srw.end +} + +func (srw *safelockReaderWriterBase) increaseInputSize(increment int) { + srw.inputSize += increment +} diff --git a/safelock/writer.go b/safelock/writer.go index 3e65611..e7b400c 100644 --- a/safelock/writer.go +++ b/safelock/writer.go @@ -5,33 +5,34 @@ import ( "fmt" "io" "strings" - - "github.com/mrf345/safelock-cli/utils" ) type safelockWriter struct { io.Writer - safelockReaderWriterBase + *safelockReaderWriterBase writer io.Writer - asyncGcm *asyncGcm + asyncGcm asyncGcm } func newWriter( pwd string, writer io.Writer, + start float64, cancel context.CancelFunc, - calc *utils.PercentCalculator, + asyncGcm asyncGcm, config EncryptionConfig, errs chan<- error, -) *safelockWriter { - return &safelockWriter{ - writer: writer, - safelockReaderWriterBase: safelockReaderWriterBase{ +) safelockWriter { + return safelockWriter{ + writer: writer, + asyncGcm: asyncGcm, + safelockReaderWriterBase: &safelockReaderWriterBase{ pwd: pwd, - calc: calc, errs: errs, cancel: cancel, config: config, + start: start, + end: 100.0, }, } } @@ -44,7 +45,7 @@ func (sw *safelockWriter) Write(chunk []byte) (written int, err error) { return written, sw.handleErr(err) } - sw.calc.OutputSize += written + sw.outputSize += written sw.blocks = append(sw.blocks, fmt.Sprintf("%d", written)) return @@ -52,14 +53,14 @@ func (sw *safelockWriter) Write(chunk []byte) (written int, err error) { func (sw *safelockWriter) WriteHeader() (err error) { sw.asyncGcm.done <- true - sw.setSize() - if 0 >= sw.size { + if 0 >= sw.outputSize { return } header := "BS;" + strings.Join(sw.blocks, ";") - headerBytes := make([]byte, sw.headerSize) + headerSize := sw.config.getHeaderSizeIn(sw.outputSize) + headerBytes := make([]byte, headerSize) headerBytes = append([]byte(header), headerBytes[len(header):]...) if _, err = sw.writer.Write(headerBytes); err != nil { @@ -69,8 +70,3 @@ func (sw *safelockWriter) WriteHeader() (err error) { return } - -func (sw *safelockWriter) setSize() { - sw.size = sw.calc.OutputSize - sw.headerSize = sw.config.getHeaderSizeIn(sw.size) -} diff --git a/utils/getExitSignals.go b/utils/getExitSignals.go new file mode 100644 index 0000000..7e73744 --- /dev/null +++ b/utils/getExitSignals.go @@ -0,0 +1,19 @@ +package utils + +import ( + "os" + "os/signal" + "syscall" +) + +// get exit signal channel, and its closer function +func GetExitSignals() (<-chan os.Signal, func()) { + signals := make(chan os.Signal, 2) + close := func() { + signal.Stop(signals) + close(signals) + } + + signal.Notify(signals, syscall.SIGTERM, syscall.SIGINT) + return signals, close +} diff --git a/utils/percentCalculator.go b/utils/percentCalculator.go deleted file mode 100644 index 0e3ef65..0000000 --- a/utils/percentCalculator.go +++ /dev/null @@ -1,48 +0,0 @@ -package utils - -import ( - "io" -) - -// helps calculating file completion percentage -type PercentCalculator struct { - OutputSize int - InputSize int - Start float64 - End float64 -} - -// create new instance of [utils.PercentCalculator] for input paths -func NewPathsCalculator(start float64) *PercentCalculator { - return &PercentCalculator{ - Start: start, - End: 100.0, - } -} - -// create new instance of [utils.PercentCalculator] for [io.Seeker] -func NewSeekerCalculator(inputSeeker io.Seeker, start float64) (pc *PercentCalculator, err error) { - pc = &PercentCalculator{ - Start: start, - End: 100.0, - } - err = pc.setInputSeekerSize(inputSeeker) - return -} - -func (pc *PercentCalculator) setInputSeekerSize(seeker io.Seeker) (err error) { - size, err := seeker.Seek(0, io.SeekEnd) - pc.InputSize = int(size) - return -} - -// get current completion percentage -func (pc *PercentCalculator) GetPercent() float64 { - percent := pc.Start + (float64(pc.OutputSize) / float64(pc.InputSize) * pc.End) - - if pc.End > percent { - return percent - } - - return pc.End -}