package meta4ra import ( "bytes" "io" "log" "os/exec" "strings" "sync" ) // Sorted by preference order. var HashesDefault = []string{ "blake3-256:b3sum", "skein-512:skein512", "skein-256:skein256", "shake128:goshake128", "shake256:goshake256", "sha-512:sha512", "sha-256:sha256", "streebog-256:streebog256sum", "streebog-512:streebog512sum", } type Hasher struct { Names []string Cmds []*exec.Cmd Ins []io.WriteCloser Outs []io.ReadCloser wg sync.WaitGroup } func NewHasher(hashes string) *Hasher { h := Hasher{} for _, hc := range strings.Split(hashes, ",") { cols := strings.SplitN(hc, ":", 2) name, cmdline := cols[0], cols[1] cmd := exec.Command(cmdline) in, err := cmd.StdinPipe() if err != nil { log.Fatalln(err) } out, err := cmd.StdoutPipe() if err != nil { log.Fatalln(err) } h.Names = append(h.Names, name) h.Ins = append(h.Ins, in) h.Outs = append(h.Outs, out) h.Cmds = append(h.Cmds, cmd) } return &h } func (h *Hasher) Start() { for _, cmd := range h.Cmds { if err := cmd.Start(); err != nil { log.Fatalln(err) } } } func (h *Hasher) Write(p []byte) (n int, err error) { h.wg.Add(len(h.Names)) for _, in := range h.Ins { go func(in io.WriteCloser) { if _, err := io.Copy(in, bytes.NewReader(p)); err != nil { log.Fatalln(err) } h.wg.Done() }(in) } h.wg.Wait() return len(p), nil } func (h *Hasher) Sums() []Hash { sums := make([]Hash, 0, len(h.Names)) for i, name := range h.Names { if err := h.Ins[i].Close(); err != nil { log.Fatalln(err) } dgst, err := io.ReadAll(h.Outs[i]) if err != nil { log.Fatalln(err) } sums = append(sums, Hash{Type: name, Hash: string(dgst[:len(dgst)-1])}) if err = h.Cmds[i].Wait(); err != nil { log.Fatalln(err) } } return sums }