##// END OF EJS Templates
revlog: prepare pure parser for being overloaded...
Raphaël Gomès -
r47136:095fa99a default
parent child Browse files
Show More
@@ -1,286 +1,287 b''
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"
37 indexfirst = struct.calcsize(b'Q')
38 sizeint = struct.calcsize(b'i')
39 indexsize = struct.calcsize(indexformatng)
40 nullitem = (0, 0, 0, -1, -1, -1, -1, nullid)
41
42
43 def gettype(q):
36 def gettype(q):
44 return int(q & 0xFFFF)
37 return int(q & 0xFFFF)
45
38
46
39
47 def offset_type(offset, type):
40 def offset_type(offset, type):
48 return int(int(offset) << 16 | type)
41 return int(int(offset) << 16 | type)
49
42
50
43
51 class BaseIndexObject(object):
44 class BaseIndexObject(object):
45 index_format = b">Qiiiiii20s12x"
46 big_int_size = struct.calcsize(b'Q')
47 int_size = struct.calcsize(b'i')
48 index_size = struct.calcsize(index_format)
49 null_item = (0, 0, 0, -1, -1, -1, -1, nullid)
50
52 @property
51 @property
53 def nodemap(self):
52 def nodemap(self):
54 msg = b"index.nodemap is deprecated, use index.[has_node|rev|get_rev]"
53 msg = b"index.nodemap is deprecated, use index.[has_node|rev|get_rev]"
55 util.nouideprecwarn(msg, b'5.3', stacklevel=2)
54 util.nouideprecwarn(msg, b'5.3', stacklevel=2)
56 return self._nodemap
55 return self._nodemap
57
56
58 @util.propertycache
57 @util.propertycache
59 def _nodemap(self):
58 def _nodemap(self):
60 nodemap = nodemaputil.NodeMap({nullid: nullrev})
59 nodemap = nodemaputil.NodeMap({nullid: nullrev})
61 for r in range(0, len(self)):
60 for r in range(0, len(self)):
62 n = self[r][7]
61 n = self[r][7]
63 nodemap[n] = r
62 nodemap[n] = r
64 return nodemap
63 return nodemap
65
64
66 def has_node(self, node):
65 def has_node(self, node):
67 """return True if the node exist in the index"""
66 """return True if the node exist in the index"""
68 return node in self._nodemap
67 return node in self._nodemap
69
68
70 def rev(self, node):
69 def rev(self, node):
71 """return a revision for a node
70 """return a revision for a node
72
71
73 If the node is unknown, raise a RevlogError"""
72 If the node is unknown, raise a RevlogError"""
74 return self._nodemap[node]
73 return self._nodemap[node]
75
74
76 def get_rev(self, node):
75 def get_rev(self, node):
77 """return a revision for a node
76 """return a revision for a node
78
77
79 If the node is unknown, return None"""
78 If the node is unknown, return None"""
80 return self._nodemap.get(node)
79 return self._nodemap.get(node)
81
80
82 def _stripnodes(self, start):
81 def _stripnodes(self, start):
83 if '_nodemap' in vars(self):
82 if '_nodemap' in vars(self):
84 for r in range(start, len(self)):
83 for r in range(start, len(self)):
85 n = self[r][7]
84 n = self[r][7]
86 del self._nodemap[n]
85 del self._nodemap[n]
87
86
88 def clearcaches(self):
87 def clearcaches(self):
89 self.__dict__.pop('_nodemap', None)
88 self.__dict__.pop('_nodemap', None)
90
89
91 def __len__(self):
90 def __len__(self):
92 return self._lgt + len(self._extra)
91 return self._lgt + len(self._extra)
93
92
94 def append(self, tup):
93 def append(self, tup):
95 if '_nodemap' in vars(self):
94 if '_nodemap' in vars(self):
96 self._nodemap[tup[7]] = len(self)
95 self._nodemap[tup[7]] = len(self)
97 data = _pack(indexformatng, *tup)
96 data = _pack(self.index_format, *tup)
98 self._extra.append(data)
97 self._extra.append(data)
99
98
100 def _check_index(self, i):
99 def _check_index(self, i):
101 if not isinstance(i, int):
100 if not isinstance(i, int):
102 raise TypeError(b"expecting int indexes")
101 raise TypeError(b"expecting int indexes")
103 if i < 0 or i >= len(self):
102 if i < 0 or i >= len(self):
104 raise IndexError
103 raise IndexError
105
104
106 def __getitem__(self, i):
105 def __getitem__(self, i):
107 if i == -1:
106 if i == -1:
108 return nullitem
107 return self.null_item
109 self._check_index(i)
108 self._check_index(i)
110 if i >= self._lgt:
109 if i >= self._lgt:
111 data = self._extra[i - self._lgt]
110 data = self._extra[i - self._lgt]
112 else:
111 else:
113 index = self._calculate_index(i)
112 index = self._calculate_index(i)
114 data = self._data[index : index + indexsize]
113 data = self._data[index : index + self.index_size]
115 r = _unpack(indexformatng, data)
114 r = _unpack(self.index_format, data)
116 if self._lgt and i == 0:
115 if self._lgt and i == 0:
117 r = (offset_type(0, gettype(r[0])),) + r[1:]
116 r = (offset_type(0, gettype(r[0])),) + r[1:]
118 return r
117 return r
119
118
120
119
121 class IndexObject(BaseIndexObject):
120 class IndexObject(BaseIndexObject):
122 def __init__(self, data):
121 def __init__(self, data):
123 assert len(data) % indexsize == 0
122 assert len(data) % self.index_size == 0
124 self._data = data
123 self._data = data
125 self._lgt = len(data) // indexsize
124 self._lgt = len(data) // self.index_size
126 self._extra = []
125 self._extra = []
127
126
128 def _calculate_index(self, i):
127 def _calculate_index(self, i):
129 return i * indexsize
128 return i * self.index_size
130
129
131 def __delitem__(self, i):
130 def __delitem__(self, i):
132 if not isinstance(i, slice) or not i.stop == -1 or i.step is not None:
131 if not isinstance(i, slice) or not i.stop == -1 or i.step is not None:
133 raise ValueError(b"deleting slices only supports a:-1 with step 1")
132 raise ValueError(b"deleting slices only supports a:-1 with step 1")
134 i = i.start
133 i = i.start
135 self._check_index(i)
134 self._check_index(i)
136 self._stripnodes(i)
135 self._stripnodes(i)
137 if i < self._lgt:
136 if i < self._lgt:
138 self._data = self._data[: i * indexsize]
137 self._data = self._data[: i * self.index_size]
139 self._lgt = i
138 self._lgt = i
140 self._extra = []
139 self._extra = []
141 else:
140 else:
142 self._extra = self._extra[: i - self._lgt]
141 self._extra = self._extra[: i - self._lgt]
143
142
144
143
145 class PersistentNodeMapIndexObject(IndexObject):
144 class PersistentNodeMapIndexObject(IndexObject):
146 """a Debug oriented class to test persistent nodemap
145 """a Debug oriented class to test persistent nodemap
147
146
148 We need a simple python object to test API and higher level behavior. See
147 We need a simple python object to test API and higher level behavior. See
149 the Rust implementation for more serious usage. This should be used only
148 the Rust implementation for more serious usage. This should be used only
150 through the dedicated `devel.persistent-nodemap` config.
149 through the dedicated `devel.persistent-nodemap` config.
151 """
150 """
152
151
153 def nodemap_data_all(self):
152 def nodemap_data_all(self):
154 """Return bytes containing a full serialization of a nodemap
153 """Return bytes containing a full serialization of a nodemap
155
154
156 The nodemap should be valid for the full set of revisions in the
155 The nodemap should be valid for the full set of revisions in the
157 index."""
156 index."""
158 return nodemaputil.persistent_data(self)
157 return nodemaputil.persistent_data(self)
159
158
160 def nodemap_data_incremental(self):
159 def nodemap_data_incremental(self):
161 """Return bytes containing a incremental update to persistent nodemap
160 """Return bytes containing a incremental update to persistent nodemap
162
161
163 This containst the data for an append-only update of the data provided
162 This containst the data for an append-only update of the data provided
164 in the last call to `update_nodemap_data`.
163 in the last call to `update_nodemap_data`.
165 """
164 """
166 if self._nm_root is None:
165 if self._nm_root is None:
167 return None
166 return None
168 docket = self._nm_docket
167 docket = self._nm_docket
169 changed, data = nodemaputil.update_persistent_data(
168 changed, data = nodemaputil.update_persistent_data(
170 self, self._nm_root, self._nm_max_idx, self._nm_docket.tip_rev
169 self, self._nm_root, self._nm_max_idx, self._nm_docket.tip_rev
171 )
170 )
172
171
173 self._nm_root = self._nm_max_idx = self._nm_docket = None
172 self._nm_root = self._nm_max_idx = self._nm_docket = None
174 return docket, changed, data
173 return docket, changed, data
175
174
176 def update_nodemap_data(self, docket, nm_data):
175 def update_nodemap_data(self, docket, nm_data):
177 """provide full block of persisted binary data for a nodemap
176 """provide full block of persisted binary data for a nodemap
178
177
179 The data are expected to come from disk. See `nodemap_data_all` for a
178 The data are expected to come from disk. See `nodemap_data_all` for a
180 produceur of such data."""
179 produceur of such data."""
181 if nm_data is not None:
180 if nm_data is not None:
182 self._nm_root, self._nm_max_idx = nodemaputil.parse_data(nm_data)
181 self._nm_root, self._nm_max_idx = nodemaputil.parse_data(nm_data)
183 if self._nm_root:
182 if self._nm_root:
184 self._nm_docket = docket
183 self._nm_docket = docket
185 else:
184 else:
186 self._nm_root = self._nm_max_idx = self._nm_docket = None
185 self._nm_root = self._nm_max_idx = self._nm_docket = None
187
186
188
187
189 class InlinedIndexObject(BaseIndexObject):
188 class InlinedIndexObject(BaseIndexObject):
190 def __init__(self, data, inline=0):
189 def __init__(self, data, inline=0):
191 self._data = data
190 self._data = data
192 self._lgt = self._inline_scan(None)
191 self._lgt = self._inline_scan(None)
193 self._inline_scan(self._lgt)
192 self._inline_scan(self._lgt)
194 self._extra = []
193 self._extra = []
195
194
196 def _inline_scan(self, lgt):
195 def _inline_scan(self, lgt):
197 off = 0
196 off = 0
198 if lgt is not None:
197 if lgt is not None:
199 self._offsets = [0] * lgt
198 self._offsets = [0] * lgt
200 count = 0
199 count = 0
201 while off <= len(self._data) - indexsize:
200 while off <= len(self._data) - self.index_size:
201 start = off + self.big_int_size
202 (s,) = struct.unpack(
202 (s,) = struct.unpack(
203 b'>i', self._data[off + indexfirst : off + sizeint + indexfirst]
203 b'>i',
204 self._data[start : start + self.int_size],
204 )
205 )
205 if lgt is not None:
206 if lgt is not None:
206 self._offsets[count] = off
207 self._offsets[count] = off
207 count += 1
208 count += 1
208 off += indexsize + s
209 off += self.index_size + s
209 if off != len(self._data):
210 if off != len(self._data):
210 raise ValueError(b"corrupted data")
211 raise ValueError(b"corrupted data")
211 return count
212 return count
212
213
213 def __delitem__(self, i):
214 def __delitem__(self, i):
214 if not isinstance(i, slice) or not i.stop == -1 or i.step is not None:
215 if not isinstance(i, slice) or not i.stop == -1 or i.step is not None:
215 raise ValueError(b"deleting slices only supports a:-1 with step 1")
216 raise ValueError(b"deleting slices only supports a:-1 with step 1")
216 i = i.start
217 i = i.start
217 self._check_index(i)
218 self._check_index(i)
218 self._stripnodes(i)
219 self._stripnodes(i)
219 if i < self._lgt:
220 if i < self._lgt:
220 self._offsets = self._offsets[:i]
221 self._offsets = self._offsets[:i]
221 self._lgt = i
222 self._lgt = i
222 self._extra = []
223 self._extra = []
223 else:
224 else:
224 self._extra = self._extra[: i - self._lgt]
225 self._extra = self._extra[: i - self._lgt]
225
226
226 def _calculate_index(self, i):
227 def _calculate_index(self, i):
227 return self._offsets[i]
228 return self._offsets[i]
228
229
229
230
230 def parse_index2(data, inline):
231 def parse_index2(data, inline):
231 if not inline:
232 if not inline:
232 return IndexObject(data), None
233 return IndexObject(data), None
233 return InlinedIndexObject(data, inline), (0, data)
234 return InlinedIndexObject(data, inline), (0, data)
234
235
235
236
236 def parse_index_devel_nodemap(data, inline):
237 def parse_index_devel_nodemap(data, inline):
237 """like parse_index2, but alway return a PersistentNodeMapIndexObject"""
238 """like parse_index2, but alway return a PersistentNodeMapIndexObject"""
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