7 # GNU General Public License version 2 or any later version. |
7 # GNU General Public License version 2 or any later version. |
8 |
8 |
9 from __future__ import absolute_import |
9 from __future__ import absolute_import |
10 |
10 |
11 import errno |
11 import errno |
12 import os |
|
13 import random |
|
14 import re |
12 import re |
15 import struct |
13 import struct |
16 |
14 |
17 from ..node import hex |
15 from ..node import hex |
18 |
16 |
19 from .. import ( |
17 from .. import ( |
20 encoding, |
|
21 error, |
18 error, |
22 pycompat, |
|
23 util, |
19 util, |
24 ) |
20 ) |
|
21 from . import docket as docket_mod |
25 |
22 |
26 |
23 |
27 class NodeMap(dict): |
24 class NodeMap(dict): |
28 def __missing__(self, x): |
25 def __missing__(self, x): |
29 raise error.RevlogError(b'unknown node: %s' % x) |
26 raise error.RevlogError(b'unknown node: %s' % x) |
279 |
276 |
280 ONDISK_VERSION = 1 |
277 ONDISK_VERSION = 1 |
281 S_VERSION = struct.Struct(">B") |
278 S_VERSION = struct.Struct(">B") |
282 S_HEADER = struct.Struct(">BQQQQ") |
279 S_HEADER = struct.Struct(">BQQQQ") |
283 |
280 |
284 ID_SIZE = 8 |
|
285 |
|
286 |
|
287 def _make_uid(): |
|
288 """return a new unique identifier. |
|
289 |
|
290 The identifier is random and composed of ascii characters.""" |
|
291 # size we "hex" the result we need half the number of bits to have a final |
|
292 # uuid of size ID_SIZE |
|
293 return hex(os.urandom(ID_SIZE // 2)) |
|
294 |
|
295 |
|
296 # some special test logic to avoid anoying random output in the test |
|
297 stable_docket_file = encoding.environ.get(b'HGTEST_DOCKETIDFILE') |
|
298 |
|
299 if stable_docket_file: |
|
300 |
|
301 def _make_uid(): |
|
302 try: |
|
303 with open(stable_docket_file, mode='rb') as f: |
|
304 seed = f.read().strip() |
|
305 except IOError as inst: |
|
306 if inst.errno != errno.ENOENT: |
|
307 raise |
|
308 seed = b'4' # chosen by a fair dice roll. garanteed to be random |
|
309 if pycompat.ispy3: |
|
310 iter_seed = iter(seed) |
|
311 else: |
|
312 iter_seed = (ord(c) for c in seed) |
|
313 # some basic circular sum hashing on 64 bits |
|
314 int_seed = 0 |
|
315 low_mask = int('1' * 35, 2) |
|
316 for i in iter_seed: |
|
317 high_part = int_seed >> 35 |
|
318 low_part = (int_seed & low_mask) << 28 |
|
319 int_seed = high_part + low_part + i |
|
320 r = random.Random() |
|
321 if pycompat.ispy3: |
|
322 r.seed(int_seed, version=1) |
|
323 else: |
|
324 r.seed(int_seed) |
|
325 # once we drop python 3.8 support we can simply use r.randbytes |
|
326 raw = r.getrandbits(ID_SIZE * 4) |
|
327 assert ID_SIZE == 8 |
|
328 p = struct.pack('>L', raw) |
|
329 new = hex(p) |
|
330 with open(stable_docket_file, 'wb') as f: |
|
331 f.write(new) |
|
332 return new |
|
333 |
|
334 |
281 |
335 class NodeMapDocket(object): |
282 class NodeMapDocket(object): |
336 """metadata associated with persistent nodemap data |
283 """metadata associated with persistent nodemap data |
337 |
284 |
338 The persistent data may come from disk or be on their way to disk. |
285 The persistent data may come from disk or be on their way to disk. |
339 """ |
286 """ |
340 |
287 |
341 def __init__(self, uid=None): |
288 def __init__(self, uid=None): |
342 if uid is None: |
289 if uid is None: |
343 uid = _make_uid() |
290 uid = docket_mod.make_uid() |
344 # a unique identifier for the data file: |
291 # a unique identifier for the data file: |
345 # - When new data are appended, it is preserved. |
292 # - When new data are appended, it is preserved. |
346 # - When a new data file is created, a new identifier is generated. |
293 # - When a new data file is created, a new identifier is generated. |
347 self.uid = uid |
294 self.uid = uid |
348 # the tipmost revision stored in the data file. This revision and all |
295 # the tipmost revision stored in the data file. This revision and all |