X-Git-Url: http://www.git.stargrave.org/?a=blobdiff_plain;f=internal%2Fhasher.go;fp=internal%2Fhasher.go;h=da54b7d519177b9674e483a56e9d136537301507;hb=be69a4fc0c240c2772a0f0bf0955b39783e4c48a;hp=0000000000000000000000000000000000000000;hpb=ce902a58a32f42801603475c67dd75da86d4502a;p=meta4ra.git diff --git a/internal/hasher.go b/internal/hasher.go new file mode 100644 index 0000000..da54b7d --- /dev/null +++ b/internal/hasher.go @@ -0,0 +1,127 @@ +// meta4ra -- Metalink 4.0 utilities +// Copyright (C) 2021-2024 Sergey Matveev +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 3 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +package internal + +import ( + "bytes" + "errors" + "io" + "os/exec" + "strings" +) + +type Hasher struct { + Names []string + Cmds []*exec.Cmd + Ins []io.WriteCloser + Outs []io.ReadCloser +} + +func NewHasher(hashes string) (*Hasher, error) { + h := Hasher{} + for _, hc := range strings.Split(hashes, ",") { + cols := strings.SplitN(hc, ":", 2) + name, cmdline := cols[0], cols[1] + if cmdline == BuiltinCmd { + newHash, exists := BuiltinHashes[name] + if !exists { + return nil, errors.New("no builtin hash: " + name) + } + b := &BuiltinHasher{h: newHash()} + h.Names = append(h.Names, name) + h.Ins = append(h.Ins, b) + h.Outs = append(h.Outs, b) + h.Cmds = append(h.Cmds, nil) + } else { + cmd := exec.Command("/bin/sh", "-e", "-c", cmdline) + in, err := cmd.StdinPipe() + if err != nil { + return &h, err + } + out, err := cmd.StdoutPipe() + if err != nil { + return &h, 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, nil +} + +func (h *Hasher) Start() (err error) { + for _, cmd := range h.Cmds { + if cmd == nil { + continue + } + err = cmd.Start() + if err != nil { + return + } + } + return nil +} + +func (h *Hasher) Stop() { + for _, cmd := range h.Cmds { + if cmd == nil || cmd.Process == nil { + continue + } + cmd.Process.Kill() + cmd.Process.Release() + } +} + +func (h *Hasher) Write(p []byte) (n int, rerr error) { + errs := make(chan error) + for _, in := range h.Ins { + go func(in io.WriteCloser) { + _, err := io.Copy(in, bytes.NewReader(p)) + errs <- err + }(in) + } + for i := 0; i < len(h.Names); i++ { + if err := <-errs; err != nil { + rerr = err + } + } + n = len(p) + return +} + +func (h *Hasher) Sums() (sums []Hash, err error) { + sums = make([]Hash, 0, len(h.Names)) + for i, name := range h.Names { + if err = h.Ins[i].Close(); err != nil { + return + } + var out []byte + out, err = io.ReadAll(h.Outs[i]) + if err != nil { + return + } + if cmd := h.Cmds[i]; cmd != nil { + if err = h.Cmds[i].Wait(); err != nil { + return + } + } + cols := strings.Fields(strings.TrimSuffix(string(out), "\n")) + sums = append(sums, Hash{Type: name, Hash: cols[0]}) + } + return +}