Patchwork [5,of,7,V3] util: add a helper class to compute digests

login
register
mail settings
Submitter Mike Hommey
Date Oct. 16, 2014, 8:22 a.m.
Message ID <f6cbabdb852b211b6779.1413447750@zenigata.glandium.org>
Download mbox | patch
Permalink /patch/6313/
State Accepted
Headers show

Comments

Mike Hommey - Oct. 16, 2014, 8:22 a.m.
# HG changeset patch
# User Mike Hommey <mh@glandium.org>
# Date 1413446571 -32400
#      Thu Oct 16 17:02:51 2014 +0900
# Node ID f6cbabdb852b211b67797d9775989f614ff56528
# Parent  1e45468e6b8ed9bbc77460cf9344d247c625f116
util: add a helper class to compute digests

It is going to be used for the remote-changegroup feature in bundle2.

Patch

diff --git a/mercurial/util.py b/mercurial/util.py
--- a/mercurial/util.py
+++ b/mercurial/util.py
@@ -112,16 +112,83 @@  def md5(s=''):
     try:
         from hashlib import md5 as _md5
     except ImportError:
         from md5 import md5 as _md5
     global md5
     md5 = _md5
     return _md5(s)
 
+DIGESTS = {
+    'md5': md5,
+    'sha1': sha1,
+}
+# List of digest types from strongest to weakest
+DIGESTS_BY_STRENGTH = ['sha1', 'md5']
+
+try:
+    import hashlib
+    DIGESTS.update({
+        'sha512': hashlib.sha512,
+    })
+    DIGESTS_BY_STRENGTH.insert(0, 'sha512')
+except ImportError:
+    pass
+
+for k in DIGESTS_BY_STRENGTH:
+    assert k in DIGESTS
+
+class digester(object):
+    """helper to compute digests.
+
+    This helper can be used to compute one or more digests given their name.
+
+    >>> d = digester(['md5', 'sha1'])
+    >>> d.update('foo')
+    >>> [k for k in d]
+    ['sha1', 'md5']
+    >>> d['md5']
+    'acbd18db4cc2f85cedef654fccc4a4d8'
+    >>> d['sha1']
+    '0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33'
+    >>> digester.preferred(['md5', 'sha1', 'sha512'])
+    'sha512' # On python >= 2.5
+    'sha1'   # On python 2.4
+    """
+
+    def __init__(self, digests, s=''):
+        self._hashes = {}
+        for k in digests:
+            if k not in DIGESTS:
+                raise Abort(_('unknown digest type: %s') % k)
+            self._hashes[k] = DIGESTS[k]()
+        if s:
+            self.update(s)
+
+    def update(self, data):
+        for h in self._hashes.values():
+            h.update(data)
+
+    def __getitem__(self, key):
+        if key not in DIGESTS:
+            raise Abort(_('unknown digest type: %s') % k)
+        return self._hashes[key].hexdigest()
+
+    def __iter__(self):
+        return iter(self._hashes)
+
+    @staticmethod
+    def preferred(supported):
+        """returns the strongest digest type in both supported and DIGESTS."""
+
+        for k in DIGESTS_BY_STRENGTH:
+            if k in supported:
+                return k
+        return None
+
 try:
     buffer = buffer
 except NameError:
     if sys.version_info[0] < 3:
         def buffer(sliceable, offset=0):
             return sliceable[offset:]
     else:
         def buffer(sliceable, offset=0):