github.com/xhghs/rclone@v1.51.1-0.20200430155106-e186a28cced8/fs/hash/hash.go (about) 1 package hash 2 3 import ( 4 "crypto/md5" 5 "crypto/sha1" 6 "encoding/hex" 7 "fmt" 8 "hash" 9 "hash/crc32" 10 "io" 11 "strings" 12 13 "github.com/jzelinskie/whirlpool" 14 "github.com/pkg/errors" 15 ) 16 17 // Type indicates a standard hashing algorithm 18 type Type int 19 20 type hashDefinition struct { 21 width int 22 name string 23 newFunc func() hash.Hash 24 hashType Type 25 } 26 27 var hashes []*hashDefinition 28 var highestType Type = 1 29 30 // RegisterHash adds a new Hash to the list and returns it Type 31 func RegisterHash(name string, width int, newFunc func() hash.Hash) Type { 32 definition := &hashDefinition{ 33 name: name, 34 width: width, 35 newFunc: newFunc, 36 hashType: highestType, 37 } 38 hashes = append(hashes, definition) 39 highestType = highestType << 1 40 41 return definition.hashType 42 } 43 44 // ErrUnsupported should be returned by filesystem, 45 // if it is requested to deliver an unsupported hash type. 46 var ErrUnsupported = errors.New("hash type not supported") 47 48 var ( 49 // None indicates no hashes are supported 50 None Type 51 52 // MD5 indicates MD5 support 53 MD5 Type 54 55 // SHA1 indicates SHA-1 support 56 SHA1 Type 57 58 // Whirlpool indicates Whirlpool support 59 Whirlpool Type 60 61 // CRC32 indicates CRC-32 support 62 CRC32 Type 63 ) 64 65 func init() { 66 MD5 = RegisterHash("MD5", 32, md5.New) 67 SHA1 = RegisterHash("SHA-1", 40, sha1.New) 68 Whirlpool = RegisterHash("Whirlpool", 128, whirlpool.New) 69 CRC32 = RegisterHash("CRC-32", 8, func() hash.Hash { return crc32.NewIEEE() }) 70 } 71 72 // Supported returns a set of all the supported hashes by 73 // HashStream and MultiHasher. 74 func Supported() Set { 75 var types []Type 76 for _, v := range hashes { 77 types = append(types, v.hashType) 78 } 79 80 return NewHashSet(types...) 81 } 82 83 // Width returns the width in characters for any HashType 84 func Width(hashType Type) int { 85 for _, v := range hashes { 86 if v.hashType == hashType { 87 return v.width 88 } 89 } 90 91 return 0 92 } 93 94 // Stream will calculate hashes of all supported hash types. 95 func Stream(r io.Reader) (map[Type]string, error) { 96 return StreamTypes(r, Supported()) 97 } 98 99 // StreamTypes will calculate hashes of the requested hash types. 100 func StreamTypes(r io.Reader, set Set) (map[Type]string, error) { 101 hashers, err := fromTypes(set) 102 if err != nil { 103 return nil, err 104 } 105 106 _, err = io.Copy(toMultiWriter(hashers), r) 107 if err != nil { 108 return nil, err 109 } 110 var ret = make(map[Type]string) 111 for k, v := range hashers { 112 ret[k] = hex.EncodeToString(v.Sum(nil)) 113 } 114 return ret, nil 115 } 116 117 // String returns a string representation of the hash type. 118 // The function will panic if the hash type is unknown. 119 func (h Type) String() string { 120 if h == None { 121 return "None" 122 } 123 124 for _, v := range hashes { 125 if v.hashType == h { 126 return v.name 127 } 128 } 129 130 err := fmt.Sprintf("internal error: unknown hash type: 0x%x", int(h)) 131 panic(err) 132 } 133 134 // Set a Type from a flag 135 func (h *Type) Set(s string) error { 136 if s == "None" { 137 *h = None 138 } 139 140 for _, v := range hashes { 141 if v.name == s { 142 *h = v.hashType 143 return nil 144 } 145 } 146 147 return errors.Errorf("Unknown hash type %q", s) 148 } 149 150 // Type of the value 151 func (h Type) Type() string { 152 return "string" 153 } 154 155 // fromTypes will return hashers for all the requested types. 156 // The types must be a subset of SupportedHashes, 157 // and this function must support all types. 158 func fromTypes(set Set) (map[Type]hash.Hash, error) { 159 if !set.SubsetOf(Supported()) { 160 return nil, errors.Errorf("requested set %08x contains unknown hash types", int(set)) 161 } 162 var hashers = make(map[Type]hash.Hash) 163 164 types := set.Array() 165 for _, t := range types { 166 for _, v := range hashes { 167 if t != v.hashType { 168 continue 169 } 170 171 hashers[t] = v.newFunc() 172 break 173 } 174 175 if hashers[t] == nil { 176 err := fmt.Sprintf("internal error: Unsupported hash type %v", t) 177 panic(err) 178 } 179 } 180 181 return hashers, nil 182 } 183 184 // toMultiWriter will return a set of hashers into a 185 // single multiwriter, where one write will update all 186 // the hashers. 187 func toMultiWriter(h map[Type]hash.Hash) io.Writer { 188 // Convert to to slice 189 var w = make([]io.Writer, 0, len(h)) 190 for _, v := range h { 191 w = append(w, v) 192 } 193 return io.MultiWriter(w...) 194 } 195 196 // A MultiHasher will construct various hashes on 197 // all incoming writes. 198 type MultiHasher struct { 199 w io.Writer 200 size int64 201 h map[Type]hash.Hash // Hashes 202 } 203 204 // NewMultiHasher will return a hash writer that will write all 205 // supported hash types. 206 func NewMultiHasher() *MultiHasher { 207 h, err := NewMultiHasherTypes(Supported()) 208 if err != nil { 209 panic("internal error: could not create multihasher") 210 } 211 return h 212 } 213 214 // NewMultiHasherTypes will return a hash writer that will write 215 // the requested hash types. 216 func NewMultiHasherTypes(set Set) (*MultiHasher, error) { 217 hashers, err := fromTypes(set) 218 if err != nil { 219 return nil, err 220 } 221 m := MultiHasher{h: hashers, w: toMultiWriter(hashers)} 222 return &m, nil 223 } 224 225 func (m *MultiHasher) Write(p []byte) (n int, err error) { 226 n, err = m.w.Write(p) 227 m.size += int64(n) 228 return n, err 229 } 230 231 // Sums returns the sums of all accumulated hashes as hex encoded 232 // strings. 233 func (m *MultiHasher) Sums() map[Type]string { 234 dst := make(map[Type]string) 235 for k, v := range m.h { 236 dst[k] = hex.EncodeToString(v.Sum(nil)) 237 } 238 return dst 239 } 240 241 // Size returns the number of bytes written 242 func (m *MultiHasher) Size() int64 { 243 return m.size 244 } 245 246 // A Set Indicates one or more hash types. 247 type Set int 248 249 // NewHashSet will create a new hash set with the hash types supplied 250 func NewHashSet(t ...Type) Set { 251 h := Set(None) 252 return h.Add(t...) 253 } 254 255 // Add one or more hash types to the set. 256 // Returns the modified hash set. 257 func (h *Set) Add(t ...Type) Set { 258 for _, v := range t { 259 *h |= Set(v) 260 } 261 return *h 262 } 263 264 // Contains returns true if the 265 func (h Set) Contains(t Type) bool { 266 return int(h)&int(t) != 0 267 } 268 269 // Overlap returns the overlapping hash types 270 func (h Set) Overlap(t Set) Set { 271 return Set(int(h) & int(t)) 272 } 273 274 // SubsetOf will return true if all types of h 275 // is present in the set c 276 func (h Set) SubsetOf(c Set) bool { 277 return int(h)|int(c) == int(c) 278 } 279 280 // GetOne will return a hash type. 281 // Currently the first is returned, but it could be 282 // improved to return the strongest. 283 func (h Set) GetOne() Type { 284 v := int(h) 285 i := uint(0) 286 for v != 0 { 287 if v&1 != 0 { 288 return Type(1 << i) 289 } 290 i++ 291 v >>= 1 292 } 293 return None 294 } 295 296 // Array returns an array of all hash types in the set 297 func (h Set) Array() (ht []Type) { 298 v := int(h) 299 i := uint(0) 300 for v != 0 { 301 if v&1 != 0 { 302 ht = append(ht, Type(1<<i)) 303 } 304 i++ 305 v >>= 1 306 } 307 return ht 308 } 309 310 // Count returns the number of hash types in the set 311 func (h Set) Count() int { 312 if int(h) == 0 { 313 return 0 314 } 315 // credit: https://code.google.com/u/arnehormann/ 316 x := uint64(h) 317 x -= (x >> 1) & 0x5555555555555555 318 x = (x>>2)&0x3333333333333333 + x&0x3333333333333333 319 x += x >> 4 320 x &= 0x0f0f0f0f0f0f0f0f 321 x *= 0x0101010101010101 322 return int(x >> 56) 323 } 324 325 // String returns a string representation of the hash set. 326 // The function will panic if it contains an unknown type. 327 func (h Set) String() string { 328 a := h.Array() 329 var r []string 330 for _, v := range a { 331 r = append(r, v.String()) 332 } 333 return "[" + strings.Join(r, ", ") + "]" 334 } 335 336 // Equals checks to see if src == dst, but ignores empty strings 337 // and returns true if either is empty. 338 func Equals(src, dst string) bool { 339 if src == "" || dst == "" { 340 return true 341 } 342 return src == dst 343 }