|
package utils |
|
|
|
import ( |
|
"crypto/md5" |
|
"crypto/sha1" |
|
"crypto/sha256" |
|
"encoding" |
|
"encoding/hex" |
|
"encoding/json" |
|
"errors" |
|
"hash" |
|
"io" |
|
|
|
"github.com/alist-org/alist/v3/internal/errs" |
|
log "github.com/sirupsen/logrus" |
|
) |
|
|
|
func GetMD5EncodeStr(data string) string { |
|
return HashData(MD5, []byte(data)) |
|
} |
|
|
|
|
|
|
|
|
|
|
|
var ErrUnsupported = errors.New("hash type not supported") |
|
|
|
|
|
type HashType struct { |
|
Width int |
|
Name string |
|
Alias string |
|
NewFunc func(...any) hash.Hash |
|
} |
|
|
|
func (ht *HashType) MarshalJSON() ([]byte, error) { |
|
return []byte(`"` + ht.Name + `"`), nil |
|
} |
|
|
|
func (ht *HashType) MarshalText() (text []byte, err error) { |
|
return []byte(ht.Name), nil |
|
} |
|
|
|
var ( |
|
_ json.Marshaler = (*HashType)(nil) |
|
|
|
|
|
|
|
_ encoding.TextMarshaler = (*HashType)(nil) |
|
|
|
) |
|
|
|
var ( |
|
name2hash = map[string]*HashType{} |
|
alias2hash = map[string]*HashType{} |
|
Supported []*HashType |
|
) |
|
|
|
|
|
func RegisterHash(name, alias string, width int, newFunc func() hash.Hash) *HashType { |
|
return RegisterHashWithParam(name, alias, width, func(a ...any) hash.Hash { return newFunc() }) |
|
} |
|
|
|
func RegisterHashWithParam(name, alias string, width int, newFunc func(...any) hash.Hash) *HashType { |
|
newType := &HashType{ |
|
Name: name, |
|
Alias: alias, |
|
Width: width, |
|
NewFunc: newFunc, |
|
} |
|
|
|
name2hash[name] = newType |
|
alias2hash[alias] = newType |
|
Supported = append(Supported, newType) |
|
return newType |
|
} |
|
|
|
var ( |
|
|
|
MD5 = RegisterHash("md5", "MD5", 32, md5.New) |
|
|
|
|
|
SHA1 = RegisterHash("sha1", "SHA-1", 40, sha1.New) |
|
|
|
|
|
SHA256 = RegisterHash("sha256", "SHA-256", 64, sha256.New) |
|
) |
|
|
|
|
|
func HashData(hashType *HashType, data []byte, params ...any) string { |
|
h := hashType.NewFunc(params...) |
|
h.Write(data) |
|
return hex.EncodeToString(h.Sum(nil)) |
|
} |
|
|
|
|
|
func HashReader(hashType *HashType, reader io.Reader, params ...any) (string, error) { |
|
h := hashType.NewFunc(params...) |
|
_, err := CopyWithBuffer(h, reader) |
|
if err != nil { |
|
return "", errs.NewErr(err, "HashReader error") |
|
} |
|
return hex.EncodeToString(h.Sum(nil)), nil |
|
} |
|
|
|
|
|
func HashFile(hashType *HashType, file io.ReadSeeker, params ...any) (string, error) { |
|
str, err := HashReader(hashType, file, params...) |
|
if err != nil { |
|
return "", err |
|
} |
|
if _, err = file.Seek(0, io.SeekStart); err != nil { |
|
return str, err |
|
} |
|
return str, nil |
|
} |
|
|
|
|
|
func fromTypes(types []*HashType) map[*HashType]hash.Hash { |
|
hashers := map[*HashType]hash.Hash{} |
|
for _, t := range types { |
|
hashers[t] = t.NewFunc() |
|
} |
|
return hashers |
|
} |
|
|
|
|
|
|
|
|
|
func toMultiWriter(h map[*HashType]hash.Hash) io.Writer { |
|
|
|
var w = make([]io.Writer, 0, len(h)) |
|
for _, v := range h { |
|
w = append(w, v) |
|
} |
|
return io.MultiWriter(w...) |
|
} |
|
|
|
|
|
type MultiHasher struct { |
|
w io.Writer |
|
size int64 |
|
h map[*HashType]hash.Hash |
|
} |
|
|
|
|
|
|
|
func NewMultiHasher(types []*HashType) *MultiHasher { |
|
hashers := fromTypes(types) |
|
m := MultiHasher{h: hashers, w: toMultiWriter(hashers)} |
|
return &m |
|
} |
|
|
|
func (m *MultiHasher) Write(p []byte) (n int, err error) { |
|
n, err = m.w.Write(p) |
|
m.size += int64(n) |
|
return n, err |
|
} |
|
|
|
func (m *MultiHasher) GetHashInfo() *HashInfo { |
|
dst := make(map[*HashType]string) |
|
for k, v := range m.h { |
|
dst[k] = hex.EncodeToString(v.Sum(nil)) |
|
} |
|
return &HashInfo{h: dst} |
|
} |
|
|
|
|
|
func (m *MultiHasher) Sum(hashType *HashType) ([]byte, error) { |
|
h, ok := m.h[hashType] |
|
if !ok { |
|
return nil, ErrUnsupported |
|
} |
|
return h.Sum(nil), nil |
|
} |
|
|
|
|
|
func (m *MultiHasher) Size() int64 { |
|
return m.size |
|
} |
|
|
|
|
|
type HashInfo struct { |
|
h map[*HashType]string `json:"hashInfo"` |
|
} |
|
|
|
func NewHashInfoByMap(h map[*HashType]string) HashInfo { |
|
return HashInfo{h} |
|
} |
|
|
|
func NewHashInfo(ht *HashType, str string) HashInfo { |
|
m := make(map[*HashType]string) |
|
if ht != nil { |
|
m[ht] = str |
|
} |
|
return HashInfo{h: m} |
|
} |
|
|
|
func (hi HashInfo) String() string { |
|
result, err := json.Marshal(hi.h) |
|
if err != nil { |
|
return "" |
|
} |
|
return string(result) |
|
} |
|
func FromString(str string) HashInfo { |
|
hi := NewHashInfo(nil, "") |
|
var tmp map[string]string |
|
err := json.Unmarshal([]byte(str), &tmp) |
|
if err != nil { |
|
log.Warnf("failed to unmarsh HashInfo from string=%s", str) |
|
} else { |
|
for k, v := range tmp { |
|
if name2hash[k] != nil && len(v) > 0 { |
|
hi.h[name2hash[k]] = v |
|
} |
|
} |
|
} |
|
|
|
return hi |
|
} |
|
func (hi HashInfo) GetHash(ht *HashType) string { |
|
return hi.h[ht] |
|
} |
|
|
|
func (hi HashInfo) Export() map[*HashType]string { |
|
return hi.h |
|
} |
|
|