| package utils |
|
|
| import ( |
| "crypto/md5" |
| "crypto/sha1" |
| "crypto/sha256" |
| "encoding" |
| "encoding/hex" |
| "encoding/json" |
| "errors" |
| "hash" |
| "io" |
| "iter" |
|
|
| "github.com/OpenListTeam/OpenList/v4/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 GetHashByName(name string) (ht *HashType, ok bool) { |
| ht, ok = name2hash[name] |
| return |
| } |
|
|
| |
| 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 |
| } |
|
|
| func (hi HashInfo) All() iter.Seq2[*HashType, string] { |
| return func(yield func(*HashType, string) bool) { |
| for hashType, hashValue := range hi.h { |
| if !yield(hashType, hashValue) { |
| return |
| } |
| } |
| } |
| } |
|
|