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  }