Spaces:
Runtime error
Runtime error
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)) | |
} | |
//inspired by "github.com/rclone/rclone/fs/hash" | |
// ErrUnsupported should be returned by filesystem, | |
// if it is requested to deliver an unsupported hash type. | |
var ErrUnsupported = errors.New("hash type not supported") | |
// HashType indicates a standard hashing algorithm | |
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) | |
//_ json.Unmarshaler = (*HashType)(nil) | |
// read/write from/to json keys | |
_ encoding.TextMarshaler = (*HashType)(nil) | |
//_ encoding.TextUnmarshaler = (*HashType)(nil) | |
) | |
var ( | |
name2hash = map[string]*HashType{} | |
alias2hash = map[string]*HashType{} | |
Supported []*HashType | |
) | |
// RegisterHash adds a new Hash to the list and returns its Type | |
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 indicates MD5 support | |
MD5 = RegisterHash("md5", "MD5", 32, md5.New) | |
// SHA1 indicates SHA-1 support | |
SHA1 = RegisterHash("sha1", "SHA-1", 40, sha1.New) | |
// SHA256 indicates SHA-256 support | |
SHA256 = RegisterHash("sha256", "SHA-256", 64, sha256.New) | |
) | |
// HashData get hash of one hashType | |
func HashData(hashType *HashType, data []byte, params ...any) string { | |
h := hashType.NewFunc(params...) | |
h.Write(data) | |
return hex.EncodeToString(h.Sum(nil)) | |
} | |
// HashReader get hash of one hashType from a reader | |
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 | |
} | |
// HashFile get hash of one hashType from a model.File | |
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 | |
} | |
// fromTypes will return hashers for all the requested types. | |
func fromTypes(types []*HashType) map[*HashType]hash.Hash { | |
hashers := map[*HashType]hash.Hash{} | |
for _, t := range types { | |
hashers[t] = t.NewFunc() | |
} | |
return hashers | |
} | |
// toMultiWriter will return a set of hashers into a | |
// single multiwriter, where one write will update all | |
// the hashers. | |
func toMultiWriter(h map[*HashType]hash.Hash) io.Writer { | |
// Convert to to slice | |
var w = make([]io.Writer, 0, len(h)) | |
for _, v := range h { | |
w = append(w, v) | |
} | |
return io.MultiWriter(w...) | |
} | |
// A MultiHasher will construct various hashes on all incoming writes. | |
type MultiHasher struct { | |
w io.Writer | |
size int64 | |
h map[*HashType]hash.Hash // Hashes | |
} | |
// NewMultiHasher will return a hash writer that will write | |
// the requested hash types. | |
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} | |
} | |
// Sum returns the specified hash from the multihasher | |
func (m *MultiHasher) Sum(hashType *HashType) ([]byte, error) { | |
h, ok := m.h[hashType] | |
if !ok { | |
return nil, ErrUnsupported | |
} | |
return h.Sum(nil), nil | |
} | |
// Size returns the number of bytes written | |
func (m *MultiHasher) Size() int64 { | |
return m.size | |
} | |
// A HashInfo contains hash string for one or more hashType | |
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 | |
} | |