diff mercurial/revlogutils/docket.py @ 47312:7ea39d633cf3

docket: move the uid logic in the `revlogutils.docket` module We want to use it for revlog-v2 (& Co), it seems more logical to have the logic lives inside the `docket` file than the `nodemap` file. Differential Revision: https://phab.mercurial-scm.org/D10755
author Pierre-Yves David <pierre-yves.david@octobus.net>
date Wed, 19 May 2021 19:57:55 +0200
parents 921648d31553
children f612db768c7a
line wrap: on
line diff
--- a/mercurial/revlogutils/docket.py	Wed May 19 17:12:06 2021 +0200
+++ b/mercurial/revlogutils/docket.py	Wed May 19 19:57:55 2021 +0200
@@ -17,10 +17,16 @@
 
 from __future__ import absolute_import
 
+import errno
+import os
+import random
 import struct
 
 from .. import (
+    encoding,
     error,
+    node,
+    pycompat,
     util,
 )
 
@@ -28,6 +34,55 @@
     constants,
 )
 
+
+def make_uid(id_size=8):
+    """return a new unique identifier.
+
+    The identifier is random and composed of ascii characters."""
+    # size we "hex" the result we need half the number of bits to have a final
+    # uuid of size ID_SIZE
+    return node.hex(os.urandom(id_size // 2))
+
+
+# some special test logic to avoid anoying random output in the test
+stable_docket_file = encoding.environ.get(b'HGTEST_UUIDFILE')
+
+if stable_docket_file:
+
+    def make_uid(id_size=8):
+        try:
+            with open(stable_docket_file, mode='rb') as f:
+                seed = f.read().strip()
+        except IOError as inst:
+            if inst.errno != errno.ENOENT:
+                raise
+            seed = b'04'  # chosen by a fair dice roll. garanteed to be random
+        if pycompat.ispy3:
+            iter_seed = iter(seed)
+        else:
+            iter_seed = (ord(c) for c in seed)
+        # some basic circular sum hashing on 64 bits
+        int_seed = 0
+        low_mask = int('1' * 35, 2)
+        for i in iter_seed:
+            high_part = int_seed >> 35
+            low_part = (int_seed & low_mask) << 28
+            int_seed = high_part + low_part + i
+        r = random.Random()
+        if pycompat.ispy3:
+            r.seed(int_seed, version=1)
+        else:
+            r.seed(int_seed)
+        # once we drop python 3.8 support we can simply use r.randbytes
+        raw = r.getrandbits(id_size * 4)
+        assert id_size == 8
+        p = struct.pack('>L', raw)
+        new = node.hex(p)
+        with open(stable_docket_file, 'wb') as f:
+            f.write(new)
+        return new
+
+
 # Docket format
 #
 # * 4 bytes: revlog version