##// END OF EJS Templates
revlog: pre-compute null revlog item for pure version...
Joerg Sonnenberger -
r46222:7baf5f79 default
parent child Browse files
Show More
@@ -1,286 +1,287
1 # parsers.py - Python implementation of parsers.c
1 # parsers.py - Python implementation of parsers.c
2 #
2 #
3 # Copyright 2009 Matt Mackall <mpm@selenic.com> and others
3 # Copyright 2009 Matt Mackall <mpm@selenic.com> and others
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from __future__ import absolute_import
8 from __future__ import absolute_import
9
9
10 import struct
10 import struct
11 import zlib
11 import zlib
12
12
13 from ..node import nullid, nullrev
13 from ..node import nullid, nullrev
14 from .. import (
14 from .. import (
15 pycompat,
15 pycompat,
16 util,
16 util,
17 )
17 )
18
18
19 from ..revlogutils import nodemap as nodemaputil
19 from ..revlogutils import nodemap as nodemaputil
20
20
21 stringio = pycompat.bytesio
21 stringio = pycompat.bytesio
22
22
23
23
24 _pack = struct.pack
24 _pack = struct.pack
25 _unpack = struct.unpack
25 _unpack = struct.unpack
26 _compress = zlib.compress
26 _compress = zlib.compress
27 _decompress = zlib.decompress
27 _decompress = zlib.decompress
28
28
29 # Some code below makes tuples directly because it's more convenient. However,
29 # Some code below makes tuples directly because it's more convenient. However,
30 # code outside this module should always use dirstatetuple.
30 # code outside this module should always use dirstatetuple.
31 def dirstatetuple(*x):
31 def dirstatetuple(*x):
32 # x is a tuple
32 # x is a tuple
33 return x
33 return x
34
34
35
35
36 indexformatng = b">Qiiiiii20s12x"
36 indexformatng = b">Qiiiiii20s12x"
37 indexfirst = struct.calcsize(b'Q')
37 indexfirst = struct.calcsize(b'Q')
38 sizeint = struct.calcsize(b'i')
38 sizeint = struct.calcsize(b'i')
39 indexsize = struct.calcsize(indexformatng)
39 indexsize = struct.calcsize(indexformatng)
40 nullitem = (0, 0, 0, -1, -1, -1, -1, nullid)
40
41
41
42
42 def gettype(q):
43 def gettype(q):
43 return int(q & 0xFFFF)
44 return int(q & 0xFFFF)
44
45
45
46
46 def offset_type(offset, type):
47 def offset_type(offset, type):
47 return int(int(offset) << 16 | type)
48 return int(int(offset) << 16 | type)
48
49
49
50
50 class BaseIndexObject(object):
51 class BaseIndexObject(object):
51 @property
52 @property
52 def nodemap(self):
53 def nodemap(self):
53 msg = b"index.nodemap is deprecated, use index.[has_node|rev|get_rev]"
54 msg = b"index.nodemap is deprecated, use index.[has_node|rev|get_rev]"
54 util.nouideprecwarn(msg, b'5.3', stacklevel=2)
55 util.nouideprecwarn(msg, b'5.3', stacklevel=2)
55 return self._nodemap
56 return self._nodemap
56
57
57 @util.propertycache
58 @util.propertycache
58 def _nodemap(self):
59 def _nodemap(self):
59 nodemap = nodemaputil.NodeMap({nullid: nullrev})
60 nodemap = nodemaputil.NodeMap({nullid: nullrev})
60 for r in range(0, len(self)):
61 for r in range(0, len(self)):
61 n = self[r][7]
62 n = self[r][7]
62 nodemap[n] = r
63 nodemap[n] = r
63 return nodemap
64 return nodemap
64
65
65 def has_node(self, node):
66 def has_node(self, node):
66 """return True if the node exist in the index"""
67 """return True if the node exist in the index"""
67 return node in self._nodemap
68 return node in self._nodemap
68
69
69 def rev(self, node):
70 def rev(self, node):
70 """return a revision for a node
71 """return a revision for a node
71
72
72 If the node is unknown, raise a RevlogError"""
73 If the node is unknown, raise a RevlogError"""
73 return self._nodemap[node]
74 return self._nodemap[node]
74
75
75 def get_rev(self, node):
76 def get_rev(self, node):
76 """return a revision for a node
77 """return a revision for a node
77
78
78 If the node is unknown, return None"""
79 If the node is unknown, return None"""
79 return self._nodemap.get(node)
80 return self._nodemap.get(node)
80
81
81 def _stripnodes(self, start):
82 def _stripnodes(self, start):
82 if '_nodemap' in vars(self):
83 if '_nodemap' in vars(self):
83 for r in range(start, len(self)):
84 for r in range(start, len(self)):
84 n = self[r][7]
85 n = self[r][7]
85 del self._nodemap[n]
86 del self._nodemap[n]
86
87
87 def clearcaches(self):
88 def clearcaches(self):
88 self.__dict__.pop('_nodemap', None)
89 self.__dict__.pop('_nodemap', None)
89
90
90 def __len__(self):
91 def __len__(self):
91 return self._lgt + len(self._extra)
92 return self._lgt + len(self._extra)
92
93
93 def append(self, tup):
94 def append(self, tup):
94 if '_nodemap' in vars(self):
95 if '_nodemap' in vars(self):
95 self._nodemap[tup[7]] = len(self)
96 self._nodemap[tup[7]] = len(self)
96 self._extra.append(tup)
97 self._extra.append(tup)
97
98
98 def _check_index(self, i):
99 def _check_index(self, i):
99 if not isinstance(i, int):
100 if not isinstance(i, int):
100 raise TypeError(b"expecting int indexes")
101 raise TypeError(b"expecting int indexes")
101 if i < 0 or i >= len(self):
102 if i < 0 or i >= len(self):
102 raise IndexError
103 raise IndexError
103
104
104 def __getitem__(self, i):
105 def __getitem__(self, i):
105 if i == -1:
106 if i == -1:
106 return (0, 0, 0, -1, -1, -1, -1, nullid)
107 return nullitem
107 self._check_index(i)
108 self._check_index(i)
108 if i >= self._lgt:
109 if i >= self._lgt:
109 return self._extra[i - self._lgt]
110 return self._extra[i - self._lgt]
110 index = self._calculate_index(i)
111 index = self._calculate_index(i)
111 r = struct.unpack(indexformatng, self._data[index : index + indexsize])
112 r = struct.unpack(indexformatng, self._data[index : index + indexsize])
112 if i == 0:
113 if i == 0:
113 e = list(r)
114 e = list(r)
114 type = gettype(e[0])
115 type = gettype(e[0])
115 e[0] = offset_type(0, type)
116 e[0] = offset_type(0, type)
116 return tuple(e)
117 return tuple(e)
117 return r
118 return r
118
119
119
120
120 class IndexObject(BaseIndexObject):
121 class IndexObject(BaseIndexObject):
121 def __init__(self, data):
122 def __init__(self, data):
122 assert len(data) % indexsize == 0
123 assert len(data) % indexsize == 0
123 self._data = data
124 self._data = data
124 self._lgt = len(data) // indexsize
125 self._lgt = len(data) // indexsize
125 self._extra = []
126 self._extra = []
126
127
127 def _calculate_index(self, i):
128 def _calculate_index(self, i):
128 return i * indexsize
129 return i * indexsize
129
130
130 def __delitem__(self, i):
131 def __delitem__(self, i):
131 if not isinstance(i, slice) or not i.stop == -1 or i.step is not None:
132 if not isinstance(i, slice) or not i.stop == -1 or i.step is not None:
132 raise ValueError(b"deleting slices only supports a:-1 with step 1")
133 raise ValueError(b"deleting slices only supports a:-1 with step 1")
133 i = i.start
134 i = i.start
134 self._check_index(i)
135 self._check_index(i)
135 self._stripnodes(i)
136 self._stripnodes(i)
136 if i < self._lgt:
137 if i < self._lgt:
137 self._data = self._data[: i * indexsize]
138 self._data = self._data[: i * indexsize]
138 self._lgt = i
139 self._lgt = i
139 self._extra = []
140 self._extra = []
140 else:
141 else:
141 self._extra = self._extra[: i - self._lgt]
142 self._extra = self._extra[: i - self._lgt]
142
143
143
144
144 class PersistentNodeMapIndexObject(IndexObject):
145 class PersistentNodeMapIndexObject(IndexObject):
145 """a Debug oriented class to test persistent nodemap
146 """a Debug oriented class to test persistent nodemap
146
147
147 We need a simple python object to test API and higher level behavior. See
148 We need a simple python object to test API and higher level behavior. See
148 the Rust implementation for more serious usage. This should be used only
149 the Rust implementation for more serious usage. This should be used only
149 through the dedicated `devel.persistent-nodemap` config.
150 through the dedicated `devel.persistent-nodemap` config.
150 """
151 """
151
152
152 def nodemap_data_all(self):
153 def nodemap_data_all(self):
153 """Return bytes containing a full serialization of a nodemap
154 """Return bytes containing a full serialization of a nodemap
154
155
155 The nodemap should be valid for the full set of revisions in the
156 The nodemap should be valid for the full set of revisions in the
156 index."""
157 index."""
157 return nodemaputil.persistent_data(self)
158 return nodemaputil.persistent_data(self)
158
159
159 def nodemap_data_incremental(self):
160 def nodemap_data_incremental(self):
160 """Return bytes containing a incremental update to persistent nodemap
161 """Return bytes containing a incremental update to persistent nodemap
161
162
162 This containst the data for an append-only update of the data provided
163 This containst the data for an append-only update of the data provided
163 in the last call to `update_nodemap_data`.
164 in the last call to `update_nodemap_data`.
164 """
165 """
165 if self._nm_root is None:
166 if self._nm_root is None:
166 return None
167 return None
167 docket = self._nm_docket
168 docket = self._nm_docket
168 changed, data = nodemaputil.update_persistent_data(
169 changed, data = nodemaputil.update_persistent_data(
169 self, self._nm_root, self._nm_max_idx, self._nm_docket.tip_rev
170 self, self._nm_root, self._nm_max_idx, self._nm_docket.tip_rev
170 )
171 )
171
172
172 self._nm_root = self._nm_max_idx = self._nm_docket = None
173 self._nm_root = self._nm_max_idx = self._nm_docket = None
173 return docket, changed, data
174 return docket, changed, data
174
175
175 def update_nodemap_data(self, docket, nm_data):
176 def update_nodemap_data(self, docket, nm_data):
176 """provide full block of persisted binary data for a nodemap
177 """provide full block of persisted binary data for a nodemap
177
178
178 The data are expected to come from disk. See `nodemap_data_all` for a
179 The data are expected to come from disk. See `nodemap_data_all` for a
179 produceur of such data."""
180 produceur of such data."""
180 if nm_data is not None:
181 if nm_data is not None:
181 self._nm_root, self._nm_max_idx = nodemaputil.parse_data(nm_data)
182 self._nm_root, self._nm_max_idx = nodemaputil.parse_data(nm_data)
182 if self._nm_root:
183 if self._nm_root:
183 self._nm_docket = docket
184 self._nm_docket = docket
184 else:
185 else:
185 self._nm_root = self._nm_max_idx = self._nm_docket = None
186 self._nm_root = self._nm_max_idx = self._nm_docket = None
186
187
187
188
188 class InlinedIndexObject(BaseIndexObject):
189 class InlinedIndexObject(BaseIndexObject):
189 def __init__(self, data, inline=0):
190 def __init__(self, data, inline=0):
190 self._data = data
191 self._data = data
191 self._lgt = self._inline_scan(None)
192 self._lgt = self._inline_scan(None)
192 self._inline_scan(self._lgt)
193 self._inline_scan(self._lgt)
193 self._extra = []
194 self._extra = []
194
195
195 def _inline_scan(self, lgt):
196 def _inline_scan(self, lgt):
196 off = 0
197 off = 0
197 if lgt is not None:
198 if lgt is not None:
198 self._offsets = [0] * lgt
199 self._offsets = [0] * lgt
199 count = 0
200 count = 0
200 while off <= len(self._data) - indexsize:
201 while off <= len(self._data) - indexsize:
201 (s,) = struct.unpack(
202 (s,) = struct.unpack(
202 b'>i', self._data[off + indexfirst : off + sizeint + indexfirst]
203 b'>i', self._data[off + indexfirst : off + sizeint + indexfirst]
203 )
204 )
204 if lgt is not None:
205 if lgt is not None:
205 self._offsets[count] = off
206 self._offsets[count] = off
206 count += 1
207 count += 1
207 off += indexsize + s
208 off += indexsize + s
208 if off != len(self._data):
209 if off != len(self._data):
209 raise ValueError(b"corrupted data")
210 raise ValueError(b"corrupted data")
210 return count
211 return count
211
212
212 def __delitem__(self, i):
213 def __delitem__(self, i):
213 if not isinstance(i, slice) or not i.stop == -1 or i.step is not None:
214 if not isinstance(i, slice) or not i.stop == -1 or i.step is not None:
214 raise ValueError(b"deleting slices only supports a:-1 with step 1")
215 raise ValueError(b"deleting slices only supports a:-1 with step 1")
215 i = i.start
216 i = i.start
216 self._check_index(i)
217 self._check_index(i)
217 self._stripnodes(i)
218 self._stripnodes(i)
218 if i < self._lgt:
219 if i < self._lgt:
219 self._offsets = self._offsets[:i]
220 self._offsets = self._offsets[:i]
220 self._lgt = i
221 self._lgt = i
221 self._extra = []
222 self._extra = []
222 else:
223 else:
223 self._extra = self._extra[: i - self._lgt]
224 self._extra = self._extra[: i - self._lgt]
224
225
225 def _calculate_index(self, i):
226 def _calculate_index(self, i):
226 return self._offsets[i]
227 return self._offsets[i]
227
228
228
229
229 def parse_index2(data, inline):
230 def parse_index2(data, inline):
230 if not inline:
231 if not inline:
231 return IndexObject(data), None
232 return IndexObject(data), None
232 return InlinedIndexObject(data, inline), (0, data)
233 return InlinedIndexObject(data, inline), (0, data)
233
234
234
235
235 def parse_index_devel_nodemap(data, inline):
236 def parse_index_devel_nodemap(data, inline):
236 """like parse_index2, but alway return a PersistentNodeMapIndexObject
237 """like parse_index2, but alway return a PersistentNodeMapIndexObject
237 """
238 """
238 return PersistentNodeMapIndexObject(data), None
239 return PersistentNodeMapIndexObject(data), None
239
240
240
241
241 def parse_dirstate(dmap, copymap, st):
242 def parse_dirstate(dmap, copymap, st):
242 parents = [st[:20], st[20:40]]
243 parents = [st[:20], st[20:40]]
243 # dereference fields so they will be local in loop
244 # dereference fields so they will be local in loop
244 format = b">cllll"
245 format = b">cllll"
245 e_size = struct.calcsize(format)
246 e_size = struct.calcsize(format)
246 pos1 = 40
247 pos1 = 40
247 l = len(st)
248 l = len(st)
248
249
249 # the inner loop
250 # the inner loop
250 while pos1 < l:
251 while pos1 < l:
251 pos2 = pos1 + e_size
252 pos2 = pos1 + e_size
252 e = _unpack(b">cllll", st[pos1:pos2]) # a literal here is faster
253 e = _unpack(b">cllll", st[pos1:pos2]) # a literal here is faster
253 pos1 = pos2 + e[4]
254 pos1 = pos2 + e[4]
254 f = st[pos2:pos1]
255 f = st[pos2:pos1]
255 if b'\0' in f:
256 if b'\0' in f:
256 f, c = f.split(b'\0')
257 f, c = f.split(b'\0')
257 copymap[f] = c
258 copymap[f] = c
258 dmap[f] = e[:4]
259 dmap[f] = e[:4]
259 return parents
260 return parents
260
261
261
262
262 def pack_dirstate(dmap, copymap, pl, now):
263 def pack_dirstate(dmap, copymap, pl, now):
263 now = int(now)
264 now = int(now)
264 cs = stringio()
265 cs = stringio()
265 write = cs.write
266 write = cs.write
266 write(b"".join(pl))
267 write(b"".join(pl))
267 for f, e in pycompat.iteritems(dmap):
268 for f, e in pycompat.iteritems(dmap):
268 if e[0] == b'n' and e[3] == now:
269 if e[0] == b'n' and e[3] == now:
269 # The file was last modified "simultaneously" with the current
270 # The file was last modified "simultaneously" with the current
270 # write to dirstate (i.e. within the same second for file-
271 # write to dirstate (i.e. within the same second for file-
271 # systems with a granularity of 1 sec). This commonly happens
272 # systems with a granularity of 1 sec). This commonly happens
272 # for at least a couple of files on 'update'.
273 # for at least a couple of files on 'update'.
273 # The user could change the file without changing its size
274 # The user could change the file without changing its size
274 # within the same second. Invalidate the file's mtime in
275 # within the same second. Invalidate the file's mtime in
275 # dirstate, forcing future 'status' calls to compare the
276 # dirstate, forcing future 'status' calls to compare the
276 # contents of the file if the size is the same. This prevents
277 # contents of the file if the size is the same. This prevents
277 # mistakenly treating such files as clean.
278 # mistakenly treating such files as clean.
278 e = dirstatetuple(e[0], e[1], e[2], -1)
279 e = dirstatetuple(e[0], e[1], e[2], -1)
279 dmap[f] = e
280 dmap[f] = e
280
281
281 if f in copymap:
282 if f in copymap:
282 f = b"%s\0%s" % (f, copymap[f])
283 f = b"%s\0%s" % (f, copymap[f])
283 e = _pack(b">cllll", e[0], e[1], e[2], e[3], len(f))
284 e = _pack(b">cllll", e[0], e[1], e[2], e[3], len(f))
284 write(e)
285 write(e)
285 write(f)
286 write(f)
286 return cs.getvalue()
287 return cs.getvalue()
General Comments 0
You need to be logged in to leave comments. Login now