|
|
- package main
-
- import (
- "crypto/md5"
- "crypto/sha1"
- "crypto/sha256"
- "crypto/sha512"
- "fmt"
- "hash"
- "io"
- "os"
- "sync"
- )
-
- // result is a message or error payload
- type result struct {
- msg string
- err error
- }
-
- // hashr exists so that we can make a thing that can return valid hash.Hash
- // interfaces.
- type hashr func() hash.Hash
-
- // hsh figures out which hash algo to use, and distributes the work of hashing
- func hsh(files []string) chan result {
- var h hashr
- switch *algo {
- case "sha1", "1":
- h = sha1.New
- case "sha256", "256":
- h = sha256.New
- case "sha512", "512":
- h = sha512.New
- case "md5":
- h = md5.New
- default:
- r := make(chan result)
- go func() {
- r <- result{err: fmt.Errorf("unsupported algorithm: %v", *algo)}
- }()
- return r
- }
-
- if len(files) == 0 {
- r := make(chan result)
- go func() {
- hsh := h()
- _, err := io.Copy(hsh, os.Stdin)
- if err != nil {
- fmt.Fprintf(os.Stderr, "%v\n", err)
- os.Exit(1)
- }
- r <- result{msg: fmt.Sprintf("%x -", hsh.Sum(nil))}
- close(r)
- }()
- return r
- }
-
- jobs := make(chan checksum)
- go func() {
- for _, name := range files {
- jobs <- checksum{filename: name}
- }
- close(jobs)
- }()
-
- res := []<-chan result{}
- for w := 0; w < *ngo; w++ {
- res = append(res, compute(h, jobs))
- }
-
- return rmerge(res)
- }
-
- // compute is the checksumming workhorse
- func compute(h hashr, jobs chan checksum) chan result {
- hsh := h()
- r := make(chan result)
- go func() {
- for job := range jobs {
- f, err := os.Open(job.filename)
- if err != nil {
- r <- result{err: err}
- continue
- }
- hsh.Reset()
- _, err = io.Copy(hsh, f)
- f.Close()
- if err != nil {
- r <- result{err: err}
- continue
- }
- r <- result{msg: fmt.Sprintf("%x %s", hsh.Sum(nil), job.filename)}
- }
- close(r)
- }()
- return r
- }
-
- // rmerge implements fan-in
- func rmerge(cs []<-chan result) chan result {
- out := make(chan result)
-
- var wg sync.WaitGroup
-
- output := func(c <-chan result) {
- for n := range c {
- out <- n
- }
- wg.Done()
- }
-
- wg.Add(len(cs))
- for _, c := range cs {
- go output(c)
- }
-
- go func() {
- wg.Wait()
- close(out)
- }()
- return out
- }
|