##// END OF EJS Templates
nodemap: have some python code writing a nodemap in persistent binary form...
marmoute -
r44788:c577bb4a default
parent child Browse files
Show More
@@ -0,0 +1,26 b''
1 ===================================
2 Test the persistent on-disk nodemap
3 ===================================
4
5
6 $ hg init test-repo
7 $ cd test-repo
8 $ hg debugbuilddag .+5000
9 $ hg debugnodemap --dump | f --sha256 --bytes=256 --hexdump --size
10 size=122880, sha256=b961925120e1c9bc345c199b2cc442abc477029fdece37ef9d99cbe59c0558b7
11 0000: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
12 0010: ff ff ff ff ff ff ff ff ff ff fa c2 ff ff ff ff |................|
13 0020: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
14 0030: ff ff ff ff ff ff ed b3 ff ff ff ff ff ff ff ff |................|
15 0040: ff ff ff ff ff ff ee 34 00 00 00 00 ff ff ff ff |.......4........|
16 0050: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
17 0060: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
18 0070: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
19 0080: ff ff ff ff ff ff f8 50 ff ff ff ff ff ff ff ff |.......P........|
20 0090: ff ff ff ff ff ff ff ff ff ff ec c7 ff ff ff ff |................|
21 00a0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
22 00b0: ff ff ff ff ff ff fa be ff ff f2 fc ff ff ff ff |................|
23 00c0: ff ff ff ff ff ff ef ea ff ff ff ff ff ff f9 17 |................|
24 00d0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
25 00e0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
26 00f0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
@@ -95,7 +95,10 b' from .utils import ('
95 stringutil,
95 stringutil,
96 )
96 )
97
97
98 from .revlogutils import deltas as deltautil
98 from .revlogutils import (
99 deltas as deltautil,
100 nodemap,
101 )
99
102
100 release = lockmod.release
103 release = lockmod.release
101
104
@@ -2082,6 +2085,20 b' def debugnamecomplete(ui, repo, *args):'
2082
2085
2083
2086
2084 @command(
2087 @command(
2088 b'debugnodemap',
2089 [(b'', b'dump', False, _(b'write persistent binary nodemap on stdin'))],
2090 )
2091 def debugnodemap(ui, repo, **opts):
2092 """write and inspect on disk nodemap
2093 """
2094 if opts['dump']:
2095 unfi = repo.unfiltered()
2096 cl = unfi.changelog
2097 data = nodemap.persistent_data(cl.index)
2098 ui.write(data)
2099
2100
2101 @command(
2085 b'debugobsolete',
2102 b'debugobsolete',
2086 [
2103 [
2087 (b'', b'flags', 0, _(b'markers flag')),
2104 (b'', b'flags', 0, _(b'markers flag')),
@@ -7,9 +7,156 b''
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 from .. import error
10
11 import struct
12
13 from .. import (
14 error,
15 node as nodemod,
16 pycompat,
17 )
11
18
12
19
13 class NodeMap(dict):
20 class NodeMap(dict):
14 def __missing__(self, x):
21 def __missing__(self, x):
15 raise error.RevlogError(b'unknown node: %s' % x)
22 raise error.RevlogError(b'unknown node: %s' % x)
23
24
25 ### Nodemap Trie
26 #
27 # This is a simple reference implementation to compute and persist a nodemap
28 # trie. This reference implementation is write only. The python version of this
29 # is not expected to be actually used, since it wont provide performance
30 # improvement over existing non-persistent C implementation.
31 #
32 # The nodemap is persisted as Trie using 4bits-address/16-entries block. each
33 # revision can be adressed using its node shortest prefix.
34 #
35 # The trie is stored as a sequence of block. Each block contains 16 entries
36 # (signed 64bit integer, big endian). Each entry can be one of the following:
37 #
38 # * value >= 0 -> index of sub-block
39 # * value == -1 -> no value
40 # * value < -1 -> a revision value: rev = -(value+10)
41 #
42 # The implementation focus on simplicity, not on performance. A Rust
43 # implementation should provide a efficient version of the same binary
44 # persistence. This reference python implementation is never meant to be
45 # extensively use in production.
46
47
48 def persistent_data(index):
49 """return the persistent binary form for a nodemap for a given index
50 """
51 trie = _build_trie(index)
52 return _persist_trie(trie)
53
54
55 S_BLOCK = struct.Struct(">" + ("l" * 16))
56
57 NO_ENTRY = -1
58 # rev 0 need to be -2 because 0 is used by block, -1 is a special value.
59 REV_OFFSET = 2
60
61
62 def _transform_rev(rev):
63 """Return the number used to represent the rev in the tree.
64
65 (or retrieve a rev number from such representation)
66
67 Note that this is an involution, a function equal to its inverse (i.e.
68 which gives the identity when applied to itself).
69 """
70 return -(rev + REV_OFFSET)
71
72
73 def _to_int(hex_digit):
74 """turn an hexadecimal digit into a proper integer"""
75 return int(hex_digit, 16)
76
77
78 def _build_trie(index):
79 """build a nodemap trie
80
81 The nodemap stores revision number for each unique prefix.
82
83 Each block is a dictionary with keys in `[0, 15]`. Values are either
84 another block or a revision number.
85 """
86 root = {}
87 for rev in range(len(index)):
88 hex = nodemod.hex(index[rev][7])
89 _insert_into_block(index, 0, root, rev, hex)
90 return root
91
92
93 def _insert_into_block(index, level, block, current_rev, current_hex):
94 """insert a new revision in a block
95
96 index: the index we are adding revision for
97 level: the depth of the current block in the trie
98 block: the block currently being considered
99 current_rev: the revision number we are adding
100 current_hex: the hexadecimal representation of the of that revision
101 """
102 hex_digit = _to_int(current_hex[level : level + 1])
103 entry = block.get(hex_digit)
104 if entry is None:
105 # no entry, simply store the revision number
106 block[hex_digit] = current_rev
107 elif isinstance(entry, dict):
108 # need to recurse to an underlying block
109 _insert_into_block(index, level + 1, entry, current_rev, current_hex)
110 else:
111 # collision with a previously unique prefix, inserting new
112 # vertices to fit both entry.
113 other_hex = nodemod.hex(index[entry][7])
114 other_rev = entry
115 new = {}
116 block[hex_digit] = new
117 _insert_into_block(index, level + 1, new, other_rev, other_hex)
118 _insert_into_block(index, level + 1, new, current_rev, current_hex)
119
120
121 def _persist_trie(root):
122 """turn a nodemap trie into persistent binary data
123
124 See `_build_trie` for nodemap trie structure"""
125 block_map = {}
126 chunks = []
127 for tn in _walk_trie(root):
128 block_map[id(tn)] = len(chunks)
129 chunks.append(_persist_block(tn, block_map))
130 return b''.join(chunks)
131
132
133 def _walk_trie(block):
134 """yield all the block in a trie
135
136 Children blocks are always yield before their parent block.
137 """
138 for (_, item) in sorted(block.items()):
139 if isinstance(item, dict):
140 for sub_block in _walk_trie(item):
141 yield sub_block
142 yield block
143
144
145 def _persist_block(block_node, block_map):
146 """produce persistent binary data for a single block
147
148 Children block are assumed to be already persisted and present in
149 block_map.
150 """
151 data = tuple(_to_value(block_node.get(i), block_map) for i in range(16))
152 return S_BLOCK.pack(*data)
153
154
155 def _to_value(item, block_map):
156 """persist any value as an integer"""
157 if item is None:
158 return NO_ENTRY
159 elif isinstance(item, dict):
160 return block_map[id(item)]
161 else:
162 return _transform_rev(item)
@@ -107,6 +107,7 b' Show debug commands if there are no othe'
107 debugmanifestfulltextcache
107 debugmanifestfulltextcache
108 debugmergestate
108 debugmergestate
109 debugnamecomplete
109 debugnamecomplete
110 debugnodemap
110 debugobsolete
111 debugobsolete
111 debugp1copies
112 debugp1copies
112 debugp2copies
113 debugp2copies
@@ -290,6 +291,7 b' Show all commands + options'
290 debugmanifestfulltextcache: clear, add
291 debugmanifestfulltextcache: clear, add
291 debugmergestate:
292 debugmergestate:
292 debugnamecomplete:
293 debugnamecomplete:
294 debugnodemap: dump
293 debugobsolete: flags, record-parents, rev, exclusive, index, delete, date, user, template
295 debugobsolete: flags, record-parents, rev, exclusive, index, delete, date, user, template
294 debugp1copies: rev
296 debugp1copies: rev
295 debugp2copies: rev
297 debugp2copies: rev
@@ -1024,6 +1024,7 b' Test list of internal help commands'
1024 print merge state
1024 print merge state
1025 debugnamecomplete
1025 debugnamecomplete
1026 complete "names" - tags, open branch names, bookmark names
1026 complete "names" - tags, open branch names, bookmark names
1027 debugnodemap write and inspect on disk nodemap
1027 debugobsolete
1028 debugobsolete
1028 create arbitrary obsolete marker
1029 create arbitrary obsolete marker
1029 debugoptADV (no help text available)
1030 debugoptADV (no help text available)
General Comments 0
You need to be logged in to leave comments. Login now