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