Spaces:
Paused
Paused
| 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 | |
| } | |