Skip to content

Instantly share code, notes, and snippets.

@dbaudisch
Created March 3, 2022 17:28
Show Gist options
  • Save dbaudisch/f1f144b172be14cfe65248f43a3adcc8 to your computer and use it in GitHub Desktop.
Save dbaudisch/f1f144b172be14cfe65248f43a3adcc8 to your computer and use it in GitHub Desktop.
My take on the concurrent checksum calculator
package main
import (
"crypto/sha256"
"fmt"
"io/fs"
"io/ioutil"
"os"
"path/filepath"
"sync"
"time"
)
const maxConcProc = 5
var wg sync.WaitGroup
type File struct {
path string
hash [32]byte
dur time.Duration
skipped bool
}
var fileCounter Counter
var skipCounter Counter
type Counter struct {
mu sync.Mutex
count uint
}
func main() {
paths := os.Args[1:]
if len(paths) == 0 {
paths = append(paths, ".")
}
files := make(chan *File, 10)
for i := 0; i < maxConcProc; i++ {
go worker(files)
wg.Add(1)
}
for _, p := range paths {
readDir(p, files)
}
close(files)
wg.Wait()
fmt.Printf("\n[Result]\nHashes calculated: %d\nFiles skipped: %d", fileCounter.count, skipCounter.count)
}
func readDir(root string, files chan<- *File) {
filepath.WalkDir(root, func(p string, e fs.DirEntry, err error) error {
if err != nil {
skipCounter.Inc()
return nil
}
if !e.IsDir() {
files <- &File{path: p}
}
return nil
})
}
func worker(files <-chan *File) {
for {
file, ok := <-files
if !ok {
break
}
file.calcHash()
if !file.skipped {
fmt.Println(file)
}
}
wg.Done()
}
func (f *File) calcHash() {
start := time.Now()
input, err := ioutil.ReadFile(f.path)
if err != nil {
f.skipped = true
skipCounter.Inc()
return
}
f.hash = sha256.Sum256(input)
f.dur = time.Since(start)
fileCounter.Inc()
}
func (f *File) String() string {
return fmt.Sprintf("%x = SHA256( %s ) took %s", f.hash, f.path, f.dur)
}
func (c *Counter) Inc() {
c.mu.Lock()
c.count++
c.mu.Unlock()
}
@hokenholz
Copy link

Nice!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment