]> Sergey Matveev's repositories - meta4ra.git/blob - hasher.go
xxd is simpler to use
[meta4ra.git] / hasher.go
1 // meta4ra -- Metalink 4.0 utilities
2 // Copyright (C) 2021-2024 Sergey Matveev <stargrave@stargrave.org>
3 //
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, version 3 of the License.
7 //
8 // This program is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11 // GNU General Public License for more details.
12 //
13 // You should have received a copy of the GNU General Public License
14 // along with this program.  If not, see <http://www.gnu.org/licenses/>.
15
16 package meta4ra
17
18 import (
19         "bytes"
20         "io"
21         "log"
22         "os/exec"
23         "strings"
24         "sync"
25 )
26
27 // Sorted by preference order.
28 var HashesDefault = []string{
29         "blake3-256:b3sum",
30         "skein-512:skein512",
31         "skein-256:skein256",
32         "shake128:goshake128",
33         "shake256:goshake256",
34         "sha-512:sha512",
35         "sha-256:sha256",
36         "streebog-256:streebog256sum",
37         "streebog-512:streebog512sum",
38 }
39
40 type Hasher struct {
41         Names []string
42         Cmds  []*exec.Cmd
43         Ins   []io.WriteCloser
44         Outs  []io.ReadCloser
45         wg    sync.WaitGroup
46 }
47
48 func NewHasher(hashes string) *Hasher {
49         h := Hasher{}
50         for _, hc := range strings.Split(hashes, ",") {
51                 cols := strings.SplitN(hc, ":", 2)
52                 name, cmdline := cols[0], cols[1]
53                 cmd := exec.Command(cmdline)
54                 in, err := cmd.StdinPipe()
55                 if err != nil {
56                         log.Fatalln(err)
57                 }
58                 out, err := cmd.StdoutPipe()
59                 if err != nil {
60                         log.Fatalln(err)
61                 }
62                 h.Names = append(h.Names, name)
63                 h.Ins = append(h.Ins, in)
64                 h.Outs = append(h.Outs, out)
65                 h.Cmds = append(h.Cmds, cmd)
66         }
67         return &h
68 }
69
70 func (h *Hasher) Start() {
71         for _, cmd := range h.Cmds {
72                 if err := cmd.Start(); err != nil {
73                         log.Fatalln(err)
74                 }
75         }
76 }
77
78 func (h *Hasher) Write(p []byte) (n int, err error) {
79         h.wg.Add(len(h.Names))
80         for _, in := range h.Ins {
81                 go func(in io.WriteCloser) {
82                         if _, err := io.Copy(in, bytes.NewReader(p)); err != nil {
83                                 log.Fatalln(err)
84                         }
85                         h.wg.Done()
86                 }(in)
87         }
88         h.wg.Wait()
89         return len(p), nil
90 }
91
92 func (h *Hasher) Sums() []Hash {
93         sums := make([]Hash, 0, len(h.Names))
94         for i, name := range h.Names {
95                 if err := h.Ins[i].Close(); err != nil {
96                         log.Fatalln(err)
97                 }
98                 dgst, err := io.ReadAll(h.Outs[i])
99                 if err != nil {
100                         log.Fatalln(err)
101                 }
102                 sums = append(sums, Hash{Type: name, Hash: string(dgst[:len(dgst)-1])})
103                 if err = h.Cmds[i].Wait(); err != nil {
104                         log.Fatalln(err)
105                 }
106         }
107         return sums
108 }