##// END OF EJS Templates
dirstate: move pure python dirstate packing to pure/parsers.py
Siddharth Agarwal -
r18567:194e63c1 default
parent child Browse files
Show More
@@ -1,816 +1,783 b''
1 # dirstate.py - working directory tracking for mercurial
1 # dirstate.py - working directory tracking for mercurial
2 #
2 #
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
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 import errno
7 import errno
8
8
9 from node import nullid
9 from node import nullid
10 from i18n import _
10 from i18n import _
11 import scmutil, util, ignore, osutil, parsers, encoding
11 import scmutil, util, ignore, osutil, parsers, encoding
12 import struct, os, stat, errno
12 import os, stat, errno
13 import cStringIO
14
13
15 _format = ">cllll"
16 propertycache = util.propertycache
14 propertycache = util.propertycache
17 filecache = scmutil.filecache
15 filecache = scmutil.filecache
18 _rangemask = 0x7fffffff
16 _rangemask = 0x7fffffff
19
17
20 class repocache(filecache):
18 class repocache(filecache):
21 """filecache for files in .hg/"""
19 """filecache for files in .hg/"""
22 def join(self, obj, fname):
20 def join(self, obj, fname):
23 return obj._opener.join(fname)
21 return obj._opener.join(fname)
24
22
25 class rootcache(filecache):
23 class rootcache(filecache):
26 """filecache for files in the repository root"""
24 """filecache for files in the repository root"""
27 def join(self, obj, fname):
25 def join(self, obj, fname):
28 return obj._join(fname)
26 return obj._join(fname)
29
27
30 def _finddirs(path):
28 def _finddirs(path):
31 pos = path.rfind('/')
29 pos = path.rfind('/')
32 while pos != -1:
30 while pos != -1:
33 yield path[:pos]
31 yield path[:pos]
34 pos = path.rfind('/', 0, pos)
32 pos = path.rfind('/', 0, pos)
35
33
36 def _incdirs(dirs, path):
34 def _incdirs(dirs, path):
37 for base in _finddirs(path):
35 for base in _finddirs(path):
38 if base in dirs:
36 if base in dirs:
39 dirs[base] += 1
37 dirs[base] += 1
40 return
38 return
41 dirs[base] = 1
39 dirs[base] = 1
42
40
43 def _decdirs(dirs, path):
41 def _decdirs(dirs, path):
44 for base in _finddirs(path):
42 for base in _finddirs(path):
45 if dirs[base] > 1:
43 if dirs[base] > 1:
46 dirs[base] -= 1
44 dirs[base] -= 1
47 return
45 return
48 del dirs[base]
46 del dirs[base]
49
47
50 class dirstate(object):
48 class dirstate(object):
51
49
52 def __init__(self, opener, ui, root, validate):
50 def __init__(self, opener, ui, root, validate):
53 '''Create a new dirstate object.
51 '''Create a new dirstate object.
54
52
55 opener is an open()-like callable that can be used to open the
53 opener is an open()-like callable that can be used to open the
56 dirstate file; root is the root of the directory tracked by
54 dirstate file; root is the root of the directory tracked by
57 the dirstate.
55 the dirstate.
58 '''
56 '''
59 self._opener = opener
57 self._opener = opener
60 self._validate = validate
58 self._validate = validate
61 self._root = root
59 self._root = root
62 self._rootdir = os.path.join(root, '')
60 self._rootdir = os.path.join(root, '')
63 self._dirty = False
61 self._dirty = False
64 self._dirtypl = False
62 self._dirtypl = False
65 self._lastnormaltime = 0
63 self._lastnormaltime = 0
66 self._ui = ui
64 self._ui = ui
67 self._filecache = {}
65 self._filecache = {}
68
66
69 @propertycache
67 @propertycache
70 def _map(self):
68 def _map(self):
71 '''Return the dirstate contents as a map from filename to
69 '''Return the dirstate contents as a map from filename to
72 (state, mode, size, time).'''
70 (state, mode, size, time).'''
73 self._read()
71 self._read()
74 return self._map
72 return self._map
75
73
76 @propertycache
74 @propertycache
77 def _copymap(self):
75 def _copymap(self):
78 self._read()
76 self._read()
79 return self._copymap
77 return self._copymap
80
78
81 @propertycache
79 @propertycache
82 def _foldmap(self):
80 def _foldmap(self):
83 f = {}
81 f = {}
84 for name in self._map:
82 for name in self._map:
85 f[util.normcase(name)] = name
83 f[util.normcase(name)] = name
86 for name in self._dirs:
84 for name in self._dirs:
87 f[util.normcase(name)] = name
85 f[util.normcase(name)] = name
88 f['.'] = '.' # prevents useless util.fspath() invocation
86 f['.'] = '.' # prevents useless util.fspath() invocation
89 return f
87 return f
90
88
91 @repocache('branch')
89 @repocache('branch')
92 def _branch(self):
90 def _branch(self):
93 try:
91 try:
94 return self._opener.read("branch").strip() or "default"
92 return self._opener.read("branch").strip() or "default"
95 except IOError, inst:
93 except IOError, inst:
96 if inst.errno != errno.ENOENT:
94 if inst.errno != errno.ENOENT:
97 raise
95 raise
98 return "default"
96 return "default"
99
97
100 @propertycache
98 @propertycache
101 def _pl(self):
99 def _pl(self):
102 try:
100 try:
103 fp = self._opener("dirstate")
101 fp = self._opener("dirstate")
104 st = fp.read(40)
102 st = fp.read(40)
105 fp.close()
103 fp.close()
106 l = len(st)
104 l = len(st)
107 if l == 40:
105 if l == 40:
108 return st[:20], st[20:40]
106 return st[:20], st[20:40]
109 elif l > 0 and l < 40:
107 elif l > 0 and l < 40:
110 raise util.Abort(_('working directory state appears damaged!'))
108 raise util.Abort(_('working directory state appears damaged!'))
111 except IOError, err:
109 except IOError, err:
112 if err.errno != errno.ENOENT:
110 if err.errno != errno.ENOENT:
113 raise
111 raise
114 return [nullid, nullid]
112 return [nullid, nullid]
115
113
116 @propertycache
114 @propertycache
117 def _dirs(self):
115 def _dirs(self):
118 dirs = {}
116 dirs = {}
119 for f, s in self._map.iteritems():
117 for f, s in self._map.iteritems():
120 if s[0] != 'r':
118 if s[0] != 'r':
121 _incdirs(dirs, f)
119 _incdirs(dirs, f)
122 return dirs
120 return dirs
123
121
124 def dirs(self):
122 def dirs(self):
125 return self._dirs
123 return self._dirs
126
124
127 @rootcache('.hgignore')
125 @rootcache('.hgignore')
128 def _ignore(self):
126 def _ignore(self):
129 files = [self._join('.hgignore')]
127 files = [self._join('.hgignore')]
130 for name, path in self._ui.configitems("ui"):
128 for name, path in self._ui.configitems("ui"):
131 if name == 'ignore' or name.startswith('ignore.'):
129 if name == 'ignore' or name.startswith('ignore.'):
132 files.append(util.expandpath(path))
130 files.append(util.expandpath(path))
133 return ignore.ignore(self._root, files, self._ui.warn)
131 return ignore.ignore(self._root, files, self._ui.warn)
134
132
135 @propertycache
133 @propertycache
136 def _slash(self):
134 def _slash(self):
137 return self._ui.configbool('ui', 'slash') and os.sep != '/'
135 return self._ui.configbool('ui', 'slash') and os.sep != '/'
138
136
139 @propertycache
137 @propertycache
140 def _checklink(self):
138 def _checklink(self):
141 return util.checklink(self._root)
139 return util.checklink(self._root)
142
140
143 @propertycache
141 @propertycache
144 def _checkexec(self):
142 def _checkexec(self):
145 return util.checkexec(self._root)
143 return util.checkexec(self._root)
146
144
147 @propertycache
145 @propertycache
148 def _checkcase(self):
146 def _checkcase(self):
149 return not util.checkcase(self._join('.hg'))
147 return not util.checkcase(self._join('.hg'))
150
148
151 def _join(self, f):
149 def _join(self, f):
152 # much faster than os.path.join()
150 # much faster than os.path.join()
153 # it's safe because f is always a relative path
151 # it's safe because f is always a relative path
154 return self._rootdir + f
152 return self._rootdir + f
155
153
156 def flagfunc(self, buildfallback):
154 def flagfunc(self, buildfallback):
157 if self._checklink and self._checkexec:
155 if self._checklink and self._checkexec:
158 def f(x):
156 def f(x):
159 p = self._join(x)
157 p = self._join(x)
160 if os.path.islink(p):
158 if os.path.islink(p):
161 return 'l'
159 return 'l'
162 if util.isexec(p):
160 if util.isexec(p):
163 return 'x'
161 return 'x'
164 return ''
162 return ''
165 return f
163 return f
166
164
167 fallback = buildfallback()
165 fallback = buildfallback()
168 if self._checklink:
166 if self._checklink:
169 def f(x):
167 def f(x):
170 if os.path.islink(self._join(x)):
168 if os.path.islink(self._join(x)):
171 return 'l'
169 return 'l'
172 if 'x' in fallback(x):
170 if 'x' in fallback(x):
173 return 'x'
171 return 'x'
174 return ''
172 return ''
175 return f
173 return f
176 if self._checkexec:
174 if self._checkexec:
177 def f(x):
175 def f(x):
178 if 'l' in fallback(x):
176 if 'l' in fallback(x):
179 return 'l'
177 return 'l'
180 if util.isexec(self._join(x)):
178 if util.isexec(self._join(x)):
181 return 'x'
179 return 'x'
182 return ''
180 return ''
183 return f
181 return f
184 else:
182 else:
185 return fallback
183 return fallback
186
184
187 def getcwd(self):
185 def getcwd(self):
188 cwd = os.getcwd()
186 cwd = os.getcwd()
189 if cwd == self._root:
187 if cwd == self._root:
190 return ''
188 return ''
191 # self._root ends with a path separator if self._root is '/' or 'C:\'
189 # self._root ends with a path separator if self._root is '/' or 'C:\'
192 rootsep = self._root
190 rootsep = self._root
193 if not util.endswithsep(rootsep):
191 if not util.endswithsep(rootsep):
194 rootsep += os.sep
192 rootsep += os.sep
195 if cwd.startswith(rootsep):
193 if cwd.startswith(rootsep):
196 return cwd[len(rootsep):]
194 return cwd[len(rootsep):]
197 else:
195 else:
198 # we're outside the repo. return an absolute path.
196 # we're outside the repo. return an absolute path.
199 return cwd
197 return cwd
200
198
201 def pathto(self, f, cwd=None):
199 def pathto(self, f, cwd=None):
202 if cwd is None:
200 if cwd is None:
203 cwd = self.getcwd()
201 cwd = self.getcwd()
204 path = util.pathto(self._root, cwd, f)
202 path = util.pathto(self._root, cwd, f)
205 if self._slash:
203 if self._slash:
206 return util.normpath(path)
204 return util.normpath(path)
207 return path
205 return path
208
206
209 def __getitem__(self, key):
207 def __getitem__(self, key):
210 '''Return the current state of key (a filename) in the dirstate.
208 '''Return the current state of key (a filename) in the dirstate.
211
209
212 States are:
210 States are:
213 n normal
211 n normal
214 m needs merging
212 m needs merging
215 r marked for removal
213 r marked for removal
216 a marked for addition
214 a marked for addition
217 ? not tracked
215 ? not tracked
218 '''
216 '''
219 return self._map.get(key, ("?",))[0]
217 return self._map.get(key, ("?",))[0]
220
218
221 def __contains__(self, key):
219 def __contains__(self, key):
222 return key in self._map
220 return key in self._map
223
221
224 def __iter__(self):
222 def __iter__(self):
225 for x in sorted(self._map):
223 for x in sorted(self._map):
226 yield x
224 yield x
227
225
228 def parents(self):
226 def parents(self):
229 return [self._validate(p) for p in self._pl]
227 return [self._validate(p) for p in self._pl]
230
228
231 def p1(self):
229 def p1(self):
232 return self._validate(self._pl[0])
230 return self._validate(self._pl[0])
233
231
234 def p2(self):
232 def p2(self):
235 return self._validate(self._pl[1])
233 return self._validate(self._pl[1])
236
234
237 def branch(self):
235 def branch(self):
238 return encoding.tolocal(self._branch)
236 return encoding.tolocal(self._branch)
239
237
240 def setparents(self, p1, p2=nullid):
238 def setparents(self, p1, p2=nullid):
241 """Set dirstate parents to p1 and p2.
239 """Set dirstate parents to p1 and p2.
242
240
243 When moving from two parents to one, 'm' merged entries a
241 When moving from two parents to one, 'm' merged entries a
244 adjusted to normal and previous copy records discarded and
242 adjusted to normal and previous copy records discarded and
245 returned by the call.
243 returned by the call.
246
244
247 See localrepo.setparents()
245 See localrepo.setparents()
248 """
246 """
249 self._dirty = self._dirtypl = True
247 self._dirty = self._dirtypl = True
250 oldp2 = self._pl[1]
248 oldp2 = self._pl[1]
251 self._pl = p1, p2
249 self._pl = p1, p2
252 copies = {}
250 copies = {}
253 if oldp2 != nullid and p2 == nullid:
251 if oldp2 != nullid and p2 == nullid:
254 # Discard 'm' markers when moving away from a merge state
252 # Discard 'm' markers when moving away from a merge state
255 for f, s in self._map.iteritems():
253 for f, s in self._map.iteritems():
256 if s[0] == 'm':
254 if s[0] == 'm':
257 if f in self._copymap:
255 if f in self._copymap:
258 copies[f] = self._copymap[f]
256 copies[f] = self._copymap[f]
259 self.normallookup(f)
257 self.normallookup(f)
260 return copies
258 return copies
261
259
262 def setbranch(self, branch):
260 def setbranch(self, branch):
263 self._branch = encoding.fromlocal(branch)
261 self._branch = encoding.fromlocal(branch)
264 f = self._opener('branch', 'w', atomictemp=True)
262 f = self._opener('branch', 'w', atomictemp=True)
265 try:
263 try:
266 f.write(self._branch + '\n')
264 f.write(self._branch + '\n')
267 f.close()
265 f.close()
268
266
269 # make sure filecache has the correct stat info for _branch after
267 # make sure filecache has the correct stat info for _branch after
270 # replacing the underlying file
268 # replacing the underlying file
271 ce = self._filecache['_branch']
269 ce = self._filecache['_branch']
272 if ce:
270 if ce:
273 ce.refresh()
271 ce.refresh()
274 except: # re-raises
272 except: # re-raises
275 f.discard()
273 f.discard()
276 raise
274 raise
277
275
278 def _read(self):
276 def _read(self):
279 self._map = {}
277 self._map = {}
280 self._copymap = {}
278 self._copymap = {}
281 try:
279 try:
282 st = self._opener.read("dirstate")
280 st = self._opener.read("dirstate")
283 except IOError, err:
281 except IOError, err:
284 if err.errno != errno.ENOENT:
282 if err.errno != errno.ENOENT:
285 raise
283 raise
286 return
284 return
287 if not st:
285 if not st:
288 return
286 return
289
287
290 p = parsers.parse_dirstate(self._map, self._copymap, st)
288 p = parsers.parse_dirstate(self._map, self._copymap, st)
291 if not self._dirtypl:
289 if not self._dirtypl:
292 self._pl = p
290 self._pl = p
293
291
294 def invalidate(self):
292 def invalidate(self):
295 for a in ("_map", "_copymap", "_foldmap", "_branch", "_pl", "_dirs",
293 for a in ("_map", "_copymap", "_foldmap", "_branch", "_pl", "_dirs",
296 "_ignore"):
294 "_ignore"):
297 if a in self.__dict__:
295 if a in self.__dict__:
298 delattr(self, a)
296 delattr(self, a)
299 self._lastnormaltime = 0
297 self._lastnormaltime = 0
300 self._dirty = False
298 self._dirty = False
301
299
302 def copy(self, source, dest):
300 def copy(self, source, dest):
303 """Mark dest as a copy of source. Unmark dest if source is None."""
301 """Mark dest as a copy of source. Unmark dest if source is None."""
304 if source == dest:
302 if source == dest:
305 return
303 return
306 self._dirty = True
304 self._dirty = True
307 if source is not None:
305 if source is not None:
308 self._copymap[dest] = source
306 self._copymap[dest] = source
309 elif dest in self._copymap:
307 elif dest in self._copymap:
310 del self._copymap[dest]
308 del self._copymap[dest]
311
309
312 def copied(self, file):
310 def copied(self, file):
313 return self._copymap.get(file, None)
311 return self._copymap.get(file, None)
314
312
315 def copies(self):
313 def copies(self):
316 return self._copymap
314 return self._copymap
317
315
318 def _droppath(self, f):
316 def _droppath(self, f):
319 if self[f] not in "?r" and "_dirs" in self.__dict__:
317 if self[f] not in "?r" and "_dirs" in self.__dict__:
320 _decdirs(self._dirs, f)
318 _decdirs(self._dirs, f)
321
319
322 def _addpath(self, f, state, mode, size, mtime):
320 def _addpath(self, f, state, mode, size, mtime):
323 oldstate = self[f]
321 oldstate = self[f]
324 if state == 'a' or oldstate == 'r':
322 if state == 'a' or oldstate == 'r':
325 scmutil.checkfilename(f)
323 scmutil.checkfilename(f)
326 if f in self._dirs:
324 if f in self._dirs:
327 raise util.Abort(_('directory %r already in dirstate') % f)
325 raise util.Abort(_('directory %r already in dirstate') % f)
328 # shadows
326 # shadows
329 for d in _finddirs(f):
327 for d in _finddirs(f):
330 if d in self._dirs:
328 if d in self._dirs:
331 break
329 break
332 if d in self._map and self[d] != 'r':
330 if d in self._map and self[d] != 'r':
333 raise util.Abort(
331 raise util.Abort(
334 _('file %r in dirstate clashes with %r') % (d, f))
332 _('file %r in dirstate clashes with %r') % (d, f))
335 if oldstate in "?r" and "_dirs" in self.__dict__:
333 if oldstate in "?r" and "_dirs" in self.__dict__:
336 _incdirs(self._dirs, f)
334 _incdirs(self._dirs, f)
337 self._dirty = True
335 self._dirty = True
338 self._map[f] = (state, mode, size, mtime)
336 self._map[f] = (state, mode, size, mtime)
339
337
340 def normal(self, f):
338 def normal(self, f):
341 '''Mark a file normal and clean.'''
339 '''Mark a file normal and clean.'''
342 s = os.lstat(self._join(f))
340 s = os.lstat(self._join(f))
343 mtime = int(s.st_mtime)
341 mtime = int(s.st_mtime)
344 self._addpath(f, 'n', s.st_mode,
342 self._addpath(f, 'n', s.st_mode,
345 s.st_size & _rangemask, mtime & _rangemask)
343 s.st_size & _rangemask, mtime & _rangemask)
346 if f in self._copymap:
344 if f in self._copymap:
347 del self._copymap[f]
345 del self._copymap[f]
348 if mtime > self._lastnormaltime:
346 if mtime > self._lastnormaltime:
349 # Remember the most recent modification timeslot for status(),
347 # Remember the most recent modification timeslot for status(),
350 # to make sure we won't miss future size-preserving file content
348 # to make sure we won't miss future size-preserving file content
351 # modifications that happen within the same timeslot.
349 # modifications that happen within the same timeslot.
352 self._lastnormaltime = mtime
350 self._lastnormaltime = mtime
353
351
354 def normallookup(self, f):
352 def normallookup(self, f):
355 '''Mark a file normal, but possibly dirty.'''
353 '''Mark a file normal, but possibly dirty.'''
356 if self._pl[1] != nullid and f in self._map:
354 if self._pl[1] != nullid and f in self._map:
357 # if there is a merge going on and the file was either
355 # if there is a merge going on and the file was either
358 # in state 'm' (-1) or coming from other parent (-2) before
356 # in state 'm' (-1) or coming from other parent (-2) before
359 # being removed, restore that state.
357 # being removed, restore that state.
360 entry = self._map[f]
358 entry = self._map[f]
361 if entry[0] == 'r' and entry[2] in (-1, -2):
359 if entry[0] == 'r' and entry[2] in (-1, -2):
362 source = self._copymap.get(f)
360 source = self._copymap.get(f)
363 if entry[2] == -1:
361 if entry[2] == -1:
364 self.merge(f)
362 self.merge(f)
365 elif entry[2] == -2:
363 elif entry[2] == -2:
366 self.otherparent(f)
364 self.otherparent(f)
367 if source:
365 if source:
368 self.copy(source, f)
366 self.copy(source, f)
369 return
367 return
370 if entry[0] == 'm' or entry[0] == 'n' and entry[2] == -2:
368 if entry[0] == 'm' or entry[0] == 'n' and entry[2] == -2:
371 return
369 return
372 self._addpath(f, 'n', 0, -1, -1)
370 self._addpath(f, 'n', 0, -1, -1)
373 if f in self._copymap:
371 if f in self._copymap:
374 del self._copymap[f]
372 del self._copymap[f]
375
373
376 def otherparent(self, f):
374 def otherparent(self, f):
377 '''Mark as coming from the other parent, always dirty.'''
375 '''Mark as coming from the other parent, always dirty.'''
378 if self._pl[1] == nullid:
376 if self._pl[1] == nullid:
379 raise util.Abort(_("setting %r to other parent "
377 raise util.Abort(_("setting %r to other parent "
380 "only allowed in merges") % f)
378 "only allowed in merges") % f)
381 self._addpath(f, 'n', 0, -2, -1)
379 self._addpath(f, 'n', 0, -2, -1)
382 if f in self._copymap:
380 if f in self._copymap:
383 del self._copymap[f]
381 del self._copymap[f]
384
382
385 def add(self, f):
383 def add(self, f):
386 '''Mark a file added.'''
384 '''Mark a file added.'''
387 self._addpath(f, 'a', 0, -1, -1)
385 self._addpath(f, 'a', 0, -1, -1)
388 if f in self._copymap:
386 if f in self._copymap:
389 del self._copymap[f]
387 del self._copymap[f]
390
388
391 def remove(self, f):
389 def remove(self, f):
392 '''Mark a file removed.'''
390 '''Mark a file removed.'''
393 self._dirty = True
391 self._dirty = True
394 self._droppath(f)
392 self._droppath(f)
395 size = 0
393 size = 0
396 if self._pl[1] != nullid and f in self._map:
394 if self._pl[1] != nullid and f in self._map:
397 # backup the previous state
395 # backup the previous state
398 entry = self._map[f]
396 entry = self._map[f]
399 if entry[0] == 'm': # merge
397 if entry[0] == 'm': # merge
400 size = -1
398 size = -1
401 elif entry[0] == 'n' and entry[2] == -2: # other parent
399 elif entry[0] == 'n' and entry[2] == -2: # other parent
402 size = -2
400 size = -2
403 self._map[f] = ('r', 0, size, 0)
401 self._map[f] = ('r', 0, size, 0)
404 if size == 0 and f in self._copymap:
402 if size == 0 and f in self._copymap:
405 del self._copymap[f]
403 del self._copymap[f]
406
404
407 def merge(self, f):
405 def merge(self, f):
408 '''Mark a file merged.'''
406 '''Mark a file merged.'''
409 if self._pl[1] == nullid:
407 if self._pl[1] == nullid:
410 return self.normallookup(f)
408 return self.normallookup(f)
411 s = os.lstat(self._join(f))
409 s = os.lstat(self._join(f))
412 self._addpath(f, 'm', s.st_mode,
410 self._addpath(f, 'm', s.st_mode,
413 s.st_size & _rangemask, int(s.st_mtime) & _rangemask)
411 s.st_size & _rangemask, int(s.st_mtime) & _rangemask)
414 if f in self._copymap:
412 if f in self._copymap:
415 del self._copymap[f]
413 del self._copymap[f]
416
414
417 def drop(self, f):
415 def drop(self, f):
418 '''Drop a file from the dirstate'''
416 '''Drop a file from the dirstate'''
419 if f in self._map:
417 if f in self._map:
420 self._dirty = True
418 self._dirty = True
421 self._droppath(f)
419 self._droppath(f)
422 del self._map[f]
420 del self._map[f]
423
421
424 def _normalize(self, path, isknown, ignoremissing=False, exists=None):
422 def _normalize(self, path, isknown, ignoremissing=False, exists=None):
425 normed = util.normcase(path)
423 normed = util.normcase(path)
426 folded = self._foldmap.get(normed, None)
424 folded = self._foldmap.get(normed, None)
427 if folded is None:
425 if folded is None:
428 if isknown:
426 if isknown:
429 folded = path
427 folded = path
430 else:
428 else:
431 if exists is None:
429 if exists is None:
432 exists = os.path.lexists(os.path.join(self._root, path))
430 exists = os.path.lexists(os.path.join(self._root, path))
433 if not exists:
431 if not exists:
434 # Maybe a path component exists
432 # Maybe a path component exists
435 if not ignoremissing and '/' in path:
433 if not ignoremissing and '/' in path:
436 d, f = path.rsplit('/', 1)
434 d, f = path.rsplit('/', 1)
437 d = self._normalize(d, isknown, ignoremissing, None)
435 d = self._normalize(d, isknown, ignoremissing, None)
438 folded = d + "/" + f
436 folded = d + "/" + f
439 else:
437 else:
440 # No path components, preserve original case
438 # No path components, preserve original case
441 folded = path
439 folded = path
442 else:
440 else:
443 # recursively normalize leading directory components
441 # recursively normalize leading directory components
444 # against dirstate
442 # against dirstate
445 if '/' in normed:
443 if '/' in normed:
446 d, f = normed.rsplit('/', 1)
444 d, f = normed.rsplit('/', 1)
447 d = self._normalize(d, isknown, ignoremissing, True)
445 d = self._normalize(d, isknown, ignoremissing, True)
448 r = self._root + "/" + d
446 r = self._root + "/" + d
449 folded = d + "/" + util.fspath(f, r)
447 folded = d + "/" + util.fspath(f, r)
450 else:
448 else:
451 folded = util.fspath(normed, self._root)
449 folded = util.fspath(normed, self._root)
452 self._foldmap[normed] = folded
450 self._foldmap[normed] = folded
453
451
454 return folded
452 return folded
455
453
456 def normalize(self, path, isknown=False, ignoremissing=False):
454 def normalize(self, path, isknown=False, ignoremissing=False):
457 '''
455 '''
458 normalize the case of a pathname when on a casefolding filesystem
456 normalize the case of a pathname when on a casefolding filesystem
459
457
460 isknown specifies whether the filename came from walking the
458 isknown specifies whether the filename came from walking the
461 disk, to avoid extra filesystem access.
459 disk, to avoid extra filesystem access.
462
460
463 If ignoremissing is True, missing path are returned
461 If ignoremissing is True, missing path are returned
464 unchanged. Otherwise, we try harder to normalize possibly
462 unchanged. Otherwise, we try harder to normalize possibly
465 existing path components.
463 existing path components.
466
464
467 The normalized case is determined based on the following precedence:
465 The normalized case is determined based on the following precedence:
468
466
469 - version of name already stored in the dirstate
467 - version of name already stored in the dirstate
470 - version of name stored on disk
468 - version of name stored on disk
471 - version provided via command arguments
469 - version provided via command arguments
472 '''
470 '''
473
471
474 if self._checkcase:
472 if self._checkcase:
475 return self._normalize(path, isknown, ignoremissing)
473 return self._normalize(path, isknown, ignoremissing)
476 return path
474 return path
477
475
478 def clear(self):
476 def clear(self):
479 self._map = {}
477 self._map = {}
480 if "_dirs" in self.__dict__:
478 if "_dirs" in self.__dict__:
481 delattr(self, "_dirs")
479 delattr(self, "_dirs")
482 self._copymap = {}
480 self._copymap = {}
483 self._pl = [nullid, nullid]
481 self._pl = [nullid, nullid]
484 self._lastnormaltime = 0
482 self._lastnormaltime = 0
485 self._dirty = True
483 self._dirty = True
486
484
487 def rebuild(self, parent, files):
485 def rebuild(self, parent, files):
488 self.clear()
486 self.clear()
489 for f in files:
487 for f in files:
490 if 'x' in files.flags(f):
488 if 'x' in files.flags(f):
491 self._map[f] = ('n', 0777, -1, 0)
489 self._map[f] = ('n', 0777, -1, 0)
492 else:
490 else:
493 self._map[f] = ('n', 0666, -1, 0)
491 self._map[f] = ('n', 0666, -1, 0)
494 self._pl = (parent, nullid)
492 self._pl = (parent, nullid)
495 self._dirty = True
493 self._dirty = True
496
494
497 def write(self):
495 def write(self):
498 if not self._dirty:
496 if not self._dirty:
499 return
497 return
500 st = self._opener("dirstate", "w", atomictemp=True)
498 st = self._opener("dirstate", "w", atomictemp=True)
501
499
502 def finish(s):
500 def finish(s):
503 st.write(s)
501 st.write(s)
504 st.close()
502 st.close()
505 self._lastnormaltime = 0
503 self._lastnormaltime = 0
506 self._dirty = self._dirtypl = False
504 self._dirty = self._dirtypl = False
507
505
508 # use the modification time of the newly created temporary file as the
506 # use the modification time of the newly created temporary file as the
509 # filesystem's notion of 'now'
507 # filesystem's notion of 'now'
510 now = util.fstat(st).st_mtime
508 now = util.fstat(st).st_mtime
511 copymap = self._copymap
509 finish(parsers.pack_dirstate(self._map, self._copymap, self._pl, now))
512 try:
513 finish(parsers.pack_dirstate(self._map, copymap, self._pl, now))
514 return
515 except AttributeError:
516 pass
517
518 now = int(now)
519 cs = cStringIO.StringIO()
520 pack = struct.pack
521 write = cs.write
522 write("".join(self._pl))
523 for f, e in self._map.iteritems():
524 if e[0] == 'n' and e[3] == now:
525 # The file was last modified "simultaneously" with the current
526 # write to dirstate (i.e. within the same second for file-
527 # systems with a granularity of 1 sec). This commonly happens
528 # for at least a couple of files on 'update'.
529 # The user could change the file without changing its size
530 # within the same second. Invalidate the file's stat data in
531 # dirstate, forcing future 'status' calls to compare the
532 # contents of the file. This prevents mistakenly treating such
533 # files as clean.
534 e = (e[0], 0, -1, -1) # mark entry as 'unset'
535 self._map[f] = e
536
537 if f in copymap:
538 f = "%s\0%s" % (f, copymap[f])
539 e = pack(_format, e[0], e[1], e[2], e[3], len(f))
540 write(e)
541 write(f)
542 finish(cs.getvalue())
543
510
544 def _dirignore(self, f):
511 def _dirignore(self, f):
545 if f == '.':
512 if f == '.':
546 return False
513 return False
547 if self._ignore(f):
514 if self._ignore(f):
548 return True
515 return True
549 for p in _finddirs(f):
516 for p in _finddirs(f):
550 if self._ignore(p):
517 if self._ignore(p):
551 return True
518 return True
552 return False
519 return False
553
520
554 def walk(self, match, subrepos, unknown, ignored):
521 def walk(self, match, subrepos, unknown, ignored):
555 '''
522 '''
556 Walk recursively through the directory tree, finding all files
523 Walk recursively through the directory tree, finding all files
557 matched by match.
524 matched by match.
558
525
559 Return a dict mapping filename to stat-like object (either
526 Return a dict mapping filename to stat-like object (either
560 mercurial.osutil.stat instance or return value of os.stat()).
527 mercurial.osutil.stat instance or return value of os.stat()).
561 '''
528 '''
562
529
563 def fwarn(f, msg):
530 def fwarn(f, msg):
564 self._ui.warn('%s: %s\n' % (self.pathto(f), msg))
531 self._ui.warn('%s: %s\n' % (self.pathto(f), msg))
565 return False
532 return False
566
533
567 def badtype(mode):
534 def badtype(mode):
568 kind = _('unknown')
535 kind = _('unknown')
569 if stat.S_ISCHR(mode):
536 if stat.S_ISCHR(mode):
570 kind = _('character device')
537 kind = _('character device')
571 elif stat.S_ISBLK(mode):
538 elif stat.S_ISBLK(mode):
572 kind = _('block device')
539 kind = _('block device')
573 elif stat.S_ISFIFO(mode):
540 elif stat.S_ISFIFO(mode):
574 kind = _('fifo')
541 kind = _('fifo')
575 elif stat.S_ISSOCK(mode):
542 elif stat.S_ISSOCK(mode):
576 kind = _('socket')
543 kind = _('socket')
577 elif stat.S_ISDIR(mode):
544 elif stat.S_ISDIR(mode):
578 kind = _('directory')
545 kind = _('directory')
579 return _('unsupported file type (type is %s)') % kind
546 return _('unsupported file type (type is %s)') % kind
580
547
581 ignore = self._ignore
548 ignore = self._ignore
582 dirignore = self._dirignore
549 dirignore = self._dirignore
583 if ignored:
550 if ignored:
584 ignore = util.never
551 ignore = util.never
585 dirignore = util.never
552 dirignore = util.never
586 elif not unknown:
553 elif not unknown:
587 # if unknown and ignored are False, skip step 2
554 # if unknown and ignored are False, skip step 2
588 ignore = util.always
555 ignore = util.always
589 dirignore = util.always
556 dirignore = util.always
590
557
591 matchfn = match.matchfn
558 matchfn = match.matchfn
592 badfn = match.bad
559 badfn = match.bad
593 dmap = self._map
560 dmap = self._map
594 normpath = util.normpath
561 normpath = util.normpath
595 listdir = osutil.listdir
562 listdir = osutil.listdir
596 lstat = os.lstat
563 lstat = os.lstat
597 getkind = stat.S_IFMT
564 getkind = stat.S_IFMT
598 dirkind = stat.S_IFDIR
565 dirkind = stat.S_IFDIR
599 regkind = stat.S_IFREG
566 regkind = stat.S_IFREG
600 lnkkind = stat.S_IFLNK
567 lnkkind = stat.S_IFLNK
601 join = self._join
568 join = self._join
602 work = []
569 work = []
603 wadd = work.append
570 wadd = work.append
604
571
605 exact = skipstep3 = False
572 exact = skipstep3 = False
606 if matchfn == match.exact: # match.exact
573 if matchfn == match.exact: # match.exact
607 exact = True
574 exact = True
608 dirignore = util.always # skip step 2
575 dirignore = util.always # skip step 2
609 elif match.files() and not match.anypats(): # match.match, no patterns
576 elif match.files() and not match.anypats(): # match.match, no patterns
610 skipstep3 = True
577 skipstep3 = True
611
578
612 if not exact and self._checkcase:
579 if not exact and self._checkcase:
613 normalize = self._normalize
580 normalize = self._normalize
614 skipstep3 = False
581 skipstep3 = False
615 else:
582 else:
616 normalize = None
583 normalize = None
617
584
618 files = sorted(match.files())
585 files = sorted(match.files())
619 subrepos.sort()
586 subrepos.sort()
620 i, j = 0, 0
587 i, j = 0, 0
621 while i < len(files) and j < len(subrepos):
588 while i < len(files) and j < len(subrepos):
622 subpath = subrepos[j] + "/"
589 subpath = subrepos[j] + "/"
623 if files[i] < subpath:
590 if files[i] < subpath:
624 i += 1
591 i += 1
625 continue
592 continue
626 while i < len(files) and files[i].startswith(subpath):
593 while i < len(files) and files[i].startswith(subpath):
627 del files[i]
594 del files[i]
628 j += 1
595 j += 1
629
596
630 if not files or '.' in files:
597 if not files or '.' in files:
631 files = ['']
598 files = ['']
632 results = dict.fromkeys(subrepos)
599 results = dict.fromkeys(subrepos)
633 results['.hg'] = None
600 results['.hg'] = None
634
601
635 # step 1: find all explicit files
602 # step 1: find all explicit files
636 for ff in files:
603 for ff in files:
637 if normalize:
604 if normalize:
638 nf = normalize(normpath(ff), False, True)
605 nf = normalize(normpath(ff), False, True)
639 else:
606 else:
640 nf = normpath(ff)
607 nf = normpath(ff)
641 if nf in results:
608 if nf in results:
642 continue
609 continue
643
610
644 try:
611 try:
645 st = lstat(join(nf))
612 st = lstat(join(nf))
646 kind = getkind(st.st_mode)
613 kind = getkind(st.st_mode)
647 if kind == dirkind:
614 if kind == dirkind:
648 skipstep3 = False
615 skipstep3 = False
649 if nf in dmap:
616 if nf in dmap:
650 #file deleted on disk but still in dirstate
617 #file deleted on disk but still in dirstate
651 results[nf] = None
618 results[nf] = None
652 match.dir(nf)
619 match.dir(nf)
653 if not dirignore(nf):
620 if not dirignore(nf):
654 wadd(nf)
621 wadd(nf)
655 elif kind == regkind or kind == lnkkind:
622 elif kind == regkind or kind == lnkkind:
656 results[nf] = st
623 results[nf] = st
657 else:
624 else:
658 badfn(ff, badtype(kind))
625 badfn(ff, badtype(kind))
659 if nf in dmap:
626 if nf in dmap:
660 results[nf] = None
627 results[nf] = None
661 except OSError, inst:
628 except OSError, inst:
662 if nf in dmap: # does it exactly match a file?
629 if nf in dmap: # does it exactly match a file?
663 results[nf] = None
630 results[nf] = None
664 else: # does it match a directory?
631 else: # does it match a directory?
665 prefix = nf + "/"
632 prefix = nf + "/"
666 for fn in dmap:
633 for fn in dmap:
667 if fn.startswith(prefix):
634 if fn.startswith(prefix):
668 match.dir(nf)
635 match.dir(nf)
669 skipstep3 = False
636 skipstep3 = False
670 break
637 break
671 else:
638 else:
672 badfn(ff, inst.strerror)
639 badfn(ff, inst.strerror)
673
640
674 # step 2: visit subdirectories
641 # step 2: visit subdirectories
675 while work:
642 while work:
676 nd = work.pop()
643 nd = work.pop()
677 skip = None
644 skip = None
678 if nd == '.':
645 if nd == '.':
679 nd = ''
646 nd = ''
680 else:
647 else:
681 skip = '.hg'
648 skip = '.hg'
682 try:
649 try:
683 entries = listdir(join(nd), stat=True, skip=skip)
650 entries = listdir(join(nd), stat=True, skip=skip)
684 except OSError, inst:
651 except OSError, inst:
685 if inst.errno in (errno.EACCES, errno.ENOENT):
652 if inst.errno in (errno.EACCES, errno.ENOENT):
686 fwarn(nd, inst.strerror)
653 fwarn(nd, inst.strerror)
687 continue
654 continue
688 raise
655 raise
689 for f, kind, st in entries:
656 for f, kind, st in entries:
690 if normalize:
657 if normalize:
691 nf = normalize(nd and (nd + "/" + f) or f, True, True)
658 nf = normalize(nd and (nd + "/" + f) or f, True, True)
692 else:
659 else:
693 nf = nd and (nd + "/" + f) or f
660 nf = nd and (nd + "/" + f) or f
694 if nf not in results:
661 if nf not in results:
695 if kind == dirkind:
662 if kind == dirkind:
696 if not ignore(nf):
663 if not ignore(nf):
697 match.dir(nf)
664 match.dir(nf)
698 wadd(nf)
665 wadd(nf)
699 if nf in dmap and matchfn(nf):
666 if nf in dmap and matchfn(nf):
700 results[nf] = None
667 results[nf] = None
701 elif kind == regkind or kind == lnkkind:
668 elif kind == regkind or kind == lnkkind:
702 if nf in dmap:
669 if nf in dmap:
703 if matchfn(nf):
670 if matchfn(nf):
704 results[nf] = st
671 results[nf] = st
705 elif matchfn(nf) and not ignore(nf):
672 elif matchfn(nf) and not ignore(nf):
706 results[nf] = st
673 results[nf] = st
707 elif nf in dmap and matchfn(nf):
674 elif nf in dmap and matchfn(nf):
708 results[nf] = None
675 results[nf] = None
709
676
710 # step 3: report unseen items in the dmap hash
677 # step 3: report unseen items in the dmap hash
711 if not skipstep3 and not exact:
678 if not skipstep3 and not exact:
712 visit = sorted([f for f in dmap if f not in results and matchfn(f)])
679 visit = sorted([f for f in dmap if f not in results and matchfn(f)])
713 nf = iter(visit).next
680 nf = iter(visit).next
714 for st in util.statfiles([join(i) for i in visit]):
681 for st in util.statfiles([join(i) for i in visit]):
715 results[nf()] = st
682 results[nf()] = st
716 for s in subrepos:
683 for s in subrepos:
717 del results[s]
684 del results[s]
718 del results['.hg']
685 del results['.hg']
719 return results
686 return results
720
687
721 def status(self, match, subrepos, ignored, clean, unknown):
688 def status(self, match, subrepos, ignored, clean, unknown):
722 '''Determine the status of the working copy relative to the
689 '''Determine the status of the working copy relative to the
723 dirstate and return a tuple of lists (unsure, modified, added,
690 dirstate and return a tuple of lists (unsure, modified, added,
724 removed, deleted, unknown, ignored, clean), where:
691 removed, deleted, unknown, ignored, clean), where:
725
692
726 unsure:
693 unsure:
727 files that might have been modified since the dirstate was
694 files that might have been modified since the dirstate was
728 written, but need to be read to be sure (size is the same
695 written, but need to be read to be sure (size is the same
729 but mtime differs)
696 but mtime differs)
730 modified:
697 modified:
731 files that have definitely been modified since the dirstate
698 files that have definitely been modified since the dirstate
732 was written (different size or mode)
699 was written (different size or mode)
733 added:
700 added:
734 files that have been explicitly added with hg add
701 files that have been explicitly added with hg add
735 removed:
702 removed:
736 files that have been explicitly removed with hg remove
703 files that have been explicitly removed with hg remove
737 deleted:
704 deleted:
738 files that have been deleted through other means ("missing")
705 files that have been deleted through other means ("missing")
739 unknown:
706 unknown:
740 files not in the dirstate that are not ignored
707 files not in the dirstate that are not ignored
741 ignored:
708 ignored:
742 files not in the dirstate that are ignored
709 files not in the dirstate that are ignored
743 (by _dirignore())
710 (by _dirignore())
744 clean:
711 clean:
745 files that have definitely not been modified since the
712 files that have definitely not been modified since the
746 dirstate was written
713 dirstate was written
747 '''
714 '''
748 listignored, listclean, listunknown = ignored, clean, unknown
715 listignored, listclean, listunknown = ignored, clean, unknown
749 lookup, modified, added, unknown, ignored = [], [], [], [], []
716 lookup, modified, added, unknown, ignored = [], [], [], [], []
750 removed, deleted, clean = [], [], []
717 removed, deleted, clean = [], [], []
751
718
752 dmap = self._map
719 dmap = self._map
753 ladd = lookup.append # aka "unsure"
720 ladd = lookup.append # aka "unsure"
754 madd = modified.append
721 madd = modified.append
755 aadd = added.append
722 aadd = added.append
756 uadd = unknown.append
723 uadd = unknown.append
757 iadd = ignored.append
724 iadd = ignored.append
758 radd = removed.append
725 radd = removed.append
759 dadd = deleted.append
726 dadd = deleted.append
760 cadd = clean.append
727 cadd = clean.append
761 mexact = match.exact
728 mexact = match.exact
762 dirignore = self._dirignore
729 dirignore = self._dirignore
763 checkexec = self._checkexec
730 checkexec = self._checkexec
764 checklink = self._checklink
731 checklink = self._checklink
765 copymap = self._copymap
732 copymap = self._copymap
766 lastnormaltime = self._lastnormaltime
733 lastnormaltime = self._lastnormaltime
767
734
768 lnkkind = stat.S_IFLNK
735 lnkkind = stat.S_IFLNK
769
736
770 for fn, st in self.walk(match, subrepos, listunknown,
737 for fn, st in self.walk(match, subrepos, listunknown,
771 listignored).iteritems():
738 listignored).iteritems():
772 if fn not in dmap:
739 if fn not in dmap:
773 if (listignored or mexact(fn)) and dirignore(fn):
740 if (listignored or mexact(fn)) and dirignore(fn):
774 if listignored:
741 if listignored:
775 iadd(fn)
742 iadd(fn)
776 elif listunknown:
743 elif listunknown:
777 uadd(fn)
744 uadd(fn)
778 continue
745 continue
779
746
780 state, mode, size, time = dmap[fn]
747 state, mode, size, time = dmap[fn]
781
748
782 if not st and state in "nma":
749 if not st and state in "nma":
783 dadd(fn)
750 dadd(fn)
784 elif state == 'n':
751 elif state == 'n':
785 # The "mode & lnkkind != lnkkind or self._checklink"
752 # The "mode & lnkkind != lnkkind or self._checklink"
786 # lines are an expansion of "islink => checklink"
753 # lines are an expansion of "islink => checklink"
787 # where islink means "is this a link?" and checklink
754 # where islink means "is this a link?" and checklink
788 # means "can we check links?".
755 # means "can we check links?".
789 mtime = int(st.st_mtime)
756 mtime = int(st.st_mtime)
790 if (size >= 0 and
757 if (size >= 0 and
791 ((size != st.st_size and size != st.st_size & _rangemask)
758 ((size != st.st_size and size != st.st_size & _rangemask)
792 or ((mode ^ st.st_mode) & 0100 and checkexec))
759 or ((mode ^ st.st_mode) & 0100 and checkexec))
793 and (mode & lnkkind != lnkkind or checklink)
760 and (mode & lnkkind != lnkkind or checklink)
794 or size == -2 # other parent
761 or size == -2 # other parent
795 or fn in copymap):
762 or fn in copymap):
796 madd(fn)
763 madd(fn)
797 elif ((time != mtime and time != mtime & _rangemask)
764 elif ((time != mtime and time != mtime & _rangemask)
798 and (mode & lnkkind != lnkkind or checklink)):
765 and (mode & lnkkind != lnkkind or checklink)):
799 ladd(fn)
766 ladd(fn)
800 elif mtime == lastnormaltime:
767 elif mtime == lastnormaltime:
801 # fn may have been changed in the same timeslot without
768 # fn may have been changed in the same timeslot without
802 # changing its size. This can happen if we quickly do
769 # changing its size. This can happen if we quickly do
803 # multiple commits in a single transaction.
770 # multiple commits in a single transaction.
804 # Force lookup, so we don't miss such a racy file change.
771 # Force lookup, so we don't miss such a racy file change.
805 ladd(fn)
772 ladd(fn)
806 elif listclean:
773 elif listclean:
807 cadd(fn)
774 cadd(fn)
808 elif state == 'm':
775 elif state == 'm':
809 madd(fn)
776 madd(fn)
810 elif state == 'a':
777 elif state == 'a':
811 aadd(fn)
778 aadd(fn)
812 elif state == 'r':
779 elif state == 'r':
813 radd(fn)
780 radd(fn)
814
781
815 return (lookup, modified, added, removed, deleted, unknown, ignored,
782 return (lookup, modified, added, removed, deleted, unknown, ignored,
816 clean)
783 clean)
@@ -1,1568 +1,1569 b''
1 /*
1 /*
2 parsers.c - efficient content parsing
2 parsers.c - efficient content parsing
3
3
4 Copyright 2008 Matt Mackall <mpm@selenic.com> and others
4 Copyright 2008 Matt Mackall <mpm@selenic.com> and others
5
5
6 This software may be used and distributed according to the terms of
6 This software may be used and distributed according to the terms of
7 the GNU General Public License, incorporated herein by reference.
7 the GNU General Public License, incorporated herein by reference.
8 */
8 */
9
9
10 #include <Python.h>
10 #include <Python.h>
11 #include <ctype.h>
11 #include <ctype.h>
12 #include <stddef.h>
12 #include <stddef.h>
13 #include <string.h>
13 #include <string.h>
14
14
15 #include "util.h"
15 #include "util.h"
16
16
17 static inline int hexdigit(const char *p, Py_ssize_t off)
17 static inline int hexdigit(const char *p, Py_ssize_t off)
18 {
18 {
19 char c = p[off];
19 char c = p[off];
20
20
21 if (c >= '0' && c <= '9')
21 if (c >= '0' && c <= '9')
22 return c - '0';
22 return c - '0';
23 if (c >= 'a' && c <= 'f')
23 if (c >= 'a' && c <= 'f')
24 return c - 'a' + 10;
24 return c - 'a' + 10;
25 if (c >= 'A' && c <= 'F')
25 if (c >= 'A' && c <= 'F')
26 return c - 'A' + 10;
26 return c - 'A' + 10;
27
27
28 PyErr_SetString(PyExc_ValueError, "input contains non-hex character");
28 PyErr_SetString(PyExc_ValueError, "input contains non-hex character");
29 return 0;
29 return 0;
30 }
30 }
31
31
32 /*
32 /*
33 * Turn a hex-encoded string into binary.
33 * Turn a hex-encoded string into binary.
34 */
34 */
35 static PyObject *unhexlify(const char *str, int len)
35 static PyObject *unhexlify(const char *str, int len)
36 {
36 {
37 PyObject *ret;
37 PyObject *ret;
38 char *d;
38 char *d;
39 int i;
39 int i;
40
40
41 ret = PyBytes_FromStringAndSize(NULL, len / 2);
41 ret = PyBytes_FromStringAndSize(NULL, len / 2);
42
42
43 if (!ret)
43 if (!ret)
44 return NULL;
44 return NULL;
45
45
46 d = PyBytes_AsString(ret);
46 d = PyBytes_AsString(ret);
47
47
48 for (i = 0; i < len;) {
48 for (i = 0; i < len;) {
49 int hi = hexdigit(str, i++);
49 int hi = hexdigit(str, i++);
50 int lo = hexdigit(str, i++);
50 int lo = hexdigit(str, i++);
51 *d++ = (hi << 4) | lo;
51 *d++ = (hi << 4) | lo;
52 }
52 }
53
53
54 return ret;
54 return ret;
55 }
55 }
56
56
57 /*
57 /*
58 * This code assumes that a manifest is stitched together with newline
58 * This code assumes that a manifest is stitched together with newline
59 * ('\n') characters.
59 * ('\n') characters.
60 */
60 */
61 static PyObject *parse_manifest(PyObject *self, PyObject *args)
61 static PyObject *parse_manifest(PyObject *self, PyObject *args)
62 {
62 {
63 PyObject *mfdict, *fdict;
63 PyObject *mfdict, *fdict;
64 char *str, *cur, *start, *zero;
64 char *str, *cur, *start, *zero;
65 int len;
65 int len;
66
66
67 if (!PyArg_ParseTuple(args, "O!O!s#:parse_manifest",
67 if (!PyArg_ParseTuple(args, "O!O!s#:parse_manifest",
68 &PyDict_Type, &mfdict,
68 &PyDict_Type, &mfdict,
69 &PyDict_Type, &fdict,
69 &PyDict_Type, &fdict,
70 &str, &len))
70 &str, &len))
71 goto quit;
71 goto quit;
72
72
73 for (start = cur = str, zero = NULL; cur < str + len; cur++) {
73 for (start = cur = str, zero = NULL; cur < str + len; cur++) {
74 PyObject *file = NULL, *node = NULL;
74 PyObject *file = NULL, *node = NULL;
75 PyObject *flags = NULL;
75 PyObject *flags = NULL;
76 ptrdiff_t nlen;
76 ptrdiff_t nlen;
77
77
78 if (!*cur) {
78 if (!*cur) {
79 zero = cur;
79 zero = cur;
80 continue;
80 continue;
81 }
81 }
82 else if (*cur != '\n')
82 else if (*cur != '\n')
83 continue;
83 continue;
84
84
85 if (!zero) {
85 if (!zero) {
86 PyErr_SetString(PyExc_ValueError,
86 PyErr_SetString(PyExc_ValueError,
87 "manifest entry has no separator");
87 "manifest entry has no separator");
88 goto quit;
88 goto quit;
89 }
89 }
90
90
91 file = PyBytes_FromStringAndSize(start, zero - start);
91 file = PyBytes_FromStringAndSize(start, zero - start);
92
92
93 if (!file)
93 if (!file)
94 goto bail;
94 goto bail;
95
95
96 nlen = cur - zero - 1;
96 nlen = cur - zero - 1;
97
97
98 node = unhexlify(zero + 1, nlen > 40 ? 40 : (int)nlen);
98 node = unhexlify(zero + 1, nlen > 40 ? 40 : (int)nlen);
99 if (!node)
99 if (!node)
100 goto bail;
100 goto bail;
101
101
102 if (nlen > 40) {
102 if (nlen > 40) {
103 flags = PyBytes_FromStringAndSize(zero + 41,
103 flags = PyBytes_FromStringAndSize(zero + 41,
104 nlen - 40);
104 nlen - 40);
105 if (!flags)
105 if (!flags)
106 goto bail;
106 goto bail;
107
107
108 if (PyDict_SetItem(fdict, file, flags) == -1)
108 if (PyDict_SetItem(fdict, file, flags) == -1)
109 goto bail;
109 goto bail;
110 }
110 }
111
111
112 if (PyDict_SetItem(mfdict, file, node) == -1)
112 if (PyDict_SetItem(mfdict, file, node) == -1)
113 goto bail;
113 goto bail;
114
114
115 start = cur + 1;
115 start = cur + 1;
116 zero = NULL;
116 zero = NULL;
117
117
118 Py_XDECREF(flags);
118 Py_XDECREF(flags);
119 Py_XDECREF(node);
119 Py_XDECREF(node);
120 Py_XDECREF(file);
120 Py_XDECREF(file);
121 continue;
121 continue;
122 bail:
122 bail:
123 Py_XDECREF(flags);
123 Py_XDECREF(flags);
124 Py_XDECREF(node);
124 Py_XDECREF(node);
125 Py_XDECREF(file);
125 Py_XDECREF(file);
126 goto quit;
126 goto quit;
127 }
127 }
128
128
129 if (len > 0 && *(cur - 1) != '\n') {
129 if (len > 0 && *(cur - 1) != '\n') {
130 PyErr_SetString(PyExc_ValueError,
130 PyErr_SetString(PyExc_ValueError,
131 "manifest contains trailing garbage");
131 "manifest contains trailing garbage");
132 goto quit;
132 goto quit;
133 }
133 }
134
134
135 Py_INCREF(Py_None);
135 Py_INCREF(Py_None);
136 return Py_None;
136 return Py_None;
137 quit:
137 quit:
138 return NULL;
138 return NULL;
139 }
139 }
140
140
141 static PyObject *parse_dirstate(PyObject *self, PyObject *args)
141 static PyObject *parse_dirstate(PyObject *self, PyObject *args)
142 {
142 {
143 PyObject *dmap, *cmap, *parents = NULL, *ret = NULL;
143 PyObject *dmap, *cmap, *parents = NULL, *ret = NULL;
144 PyObject *fname = NULL, *cname = NULL, *entry = NULL;
144 PyObject *fname = NULL, *cname = NULL, *entry = NULL;
145 char *str, *cur, *end, *cpos;
145 char *str, *cur, *end, *cpos;
146 int state, mode, size, mtime;
146 int state, mode, size, mtime;
147 unsigned int flen;
147 unsigned int flen;
148 int len;
148 int len;
149
149
150 if (!PyArg_ParseTuple(args, "O!O!s#:parse_dirstate",
150 if (!PyArg_ParseTuple(args, "O!O!s#:parse_dirstate",
151 &PyDict_Type, &dmap,
151 &PyDict_Type, &dmap,
152 &PyDict_Type, &cmap,
152 &PyDict_Type, &cmap,
153 &str, &len))
153 &str, &len))
154 goto quit;
154 goto quit;
155
155
156 /* read parents */
156 /* read parents */
157 if (len < 40)
157 if (len < 40)
158 goto quit;
158 goto quit;
159
159
160 parents = Py_BuildValue("s#s#", str, 20, str + 20, 20);
160 parents = Py_BuildValue("s#s#", str, 20, str + 20, 20);
161 if (!parents)
161 if (!parents)
162 goto quit;
162 goto quit;
163
163
164 /* read filenames */
164 /* read filenames */
165 cur = str + 40;
165 cur = str + 40;
166 end = str + len;
166 end = str + len;
167
167
168 while (cur < end - 17) {
168 while (cur < end - 17) {
169 /* unpack header */
169 /* unpack header */
170 state = *cur;
170 state = *cur;
171 mode = getbe32(cur + 1);
171 mode = getbe32(cur + 1);
172 size = getbe32(cur + 5);
172 size = getbe32(cur + 5);
173 mtime = getbe32(cur + 9);
173 mtime = getbe32(cur + 9);
174 flen = getbe32(cur + 13);
174 flen = getbe32(cur + 13);
175 cur += 17;
175 cur += 17;
176 if (cur + flen > end || cur + flen < cur) {
176 if (cur + flen > end || cur + flen < cur) {
177 PyErr_SetString(PyExc_ValueError, "overflow in dirstate");
177 PyErr_SetString(PyExc_ValueError, "overflow in dirstate");
178 goto quit;
178 goto quit;
179 }
179 }
180
180
181 entry = Py_BuildValue("ciii", state, mode, size, mtime);
181 entry = Py_BuildValue("ciii", state, mode, size, mtime);
182 if (!entry)
182 if (!entry)
183 goto quit;
183 goto quit;
184 PyObject_GC_UnTrack(entry); /* don't waste time with this */
184 PyObject_GC_UnTrack(entry); /* don't waste time with this */
185
185
186 cpos = memchr(cur, 0, flen);
186 cpos = memchr(cur, 0, flen);
187 if (cpos) {
187 if (cpos) {
188 fname = PyBytes_FromStringAndSize(cur, cpos - cur);
188 fname = PyBytes_FromStringAndSize(cur, cpos - cur);
189 cname = PyBytes_FromStringAndSize(cpos + 1,
189 cname = PyBytes_FromStringAndSize(cpos + 1,
190 flen - (cpos - cur) - 1);
190 flen - (cpos - cur) - 1);
191 if (!fname || !cname ||
191 if (!fname || !cname ||
192 PyDict_SetItem(cmap, fname, cname) == -1 ||
192 PyDict_SetItem(cmap, fname, cname) == -1 ||
193 PyDict_SetItem(dmap, fname, entry) == -1)
193 PyDict_SetItem(dmap, fname, entry) == -1)
194 goto quit;
194 goto quit;
195 Py_DECREF(cname);
195 Py_DECREF(cname);
196 } else {
196 } else {
197 fname = PyBytes_FromStringAndSize(cur, flen);
197 fname = PyBytes_FromStringAndSize(cur, flen);
198 if (!fname ||
198 if (!fname ||
199 PyDict_SetItem(dmap, fname, entry) == -1)
199 PyDict_SetItem(dmap, fname, entry) == -1)
200 goto quit;
200 goto quit;
201 }
201 }
202 cur += flen;
202 cur += flen;
203 Py_DECREF(fname);
203 Py_DECREF(fname);
204 Py_DECREF(entry);
204 Py_DECREF(entry);
205 fname = cname = entry = NULL;
205 fname = cname = entry = NULL;
206 }
206 }
207
207
208 ret = parents;
208 ret = parents;
209 Py_INCREF(ret);
209 Py_INCREF(ret);
210 quit:
210 quit:
211 Py_XDECREF(fname);
211 Py_XDECREF(fname);
212 Py_XDECREF(cname);
212 Py_XDECREF(cname);
213 Py_XDECREF(entry);
213 Py_XDECREF(entry);
214 Py_XDECREF(parents);
214 Py_XDECREF(parents);
215 return ret;
215 return ret;
216 }
216 }
217
217
218 static inline int getintat(PyObject *tuple, int off, uint32_t *v)
218 static inline int getintat(PyObject *tuple, int off, uint32_t *v)
219 {
219 {
220 PyObject *o = PyTuple_GET_ITEM(tuple, off);
220 PyObject *o = PyTuple_GET_ITEM(tuple, off);
221 long val;
221 long val;
222
222
223 if (PyInt_Check(o))
223 if (PyInt_Check(o))
224 val = PyInt_AS_LONG(o);
224 val = PyInt_AS_LONG(o);
225 else if (PyLong_Check(o)) {
225 else if (PyLong_Check(o)) {
226 val = PyLong_AsLong(o);
226 val = PyLong_AsLong(o);
227 if (val == -1 && PyErr_Occurred())
227 if (val == -1 && PyErr_Occurred())
228 return -1;
228 return -1;
229 } else {
229 } else {
230 PyErr_SetString(PyExc_TypeError, "expected an int or long");
230 PyErr_SetString(PyExc_TypeError, "expected an int or long");
231 return -1;
231 return -1;
232 }
232 }
233 if (LONG_MAX > INT_MAX && (val > INT_MAX || val < INT_MIN)) {
233 if (LONG_MAX > INT_MAX && (val > INT_MAX || val < INT_MIN)) {
234 PyErr_SetString(PyExc_OverflowError,
234 PyErr_SetString(PyExc_OverflowError,
235 "Python value to large to convert to uint32_t");
235 "Python value to large to convert to uint32_t");
236 return -1;
236 return -1;
237 }
237 }
238 *v = (uint32_t)val;
238 *v = (uint32_t)val;
239 return 0;
239 return 0;
240 }
240 }
241
241
242 static PyObject *dirstate_unset;
242 static PyObject *dirstate_unset;
243
243
244 /*
244 /*
245 * Efficiently pack a dirstate object into its on-disk format.
245 * Efficiently pack a dirstate object into its on-disk format.
246 */
246 */
247 static PyObject *pack_dirstate(PyObject *self, PyObject *args)
247 static PyObject *pack_dirstate(PyObject *self, PyObject *args)
248 {
248 {
249 PyObject *packobj = NULL;
249 PyObject *packobj = NULL;
250 PyObject *map, *copymap, *pl;
250 PyObject *map, *copymap, *pl;
251 Py_ssize_t nbytes, pos, l;
251 Py_ssize_t nbytes, pos, l;
252 PyObject *k, *v, *pn;
252 PyObject *k, *v, *pn;
253 char *p, *s;
253 char *p, *s;
254 double now;
254 double now;
255
255
256 if (!PyArg_ParseTuple(args, "O!O!Od:pack_dirstate",
256 if (!PyArg_ParseTuple(args, "O!O!Od:pack_dirstate",
257 &PyDict_Type, &map, &PyDict_Type, &copymap,
257 &PyDict_Type, &map, &PyDict_Type, &copymap,
258 &pl, &now))
258 &pl, &now))
259 return NULL;
259 return NULL;
260
260
261 if (!PySequence_Check(pl) || PySequence_Size(pl) != 2) {
261 if (!PySequence_Check(pl) || PySequence_Size(pl) != 2) {
262 PyErr_SetString(PyExc_TypeError, "expected 2-element sequence");
262 PyErr_SetString(PyExc_TypeError, "expected 2-element sequence");
263 return NULL;
263 return NULL;
264 }
264 }
265
265
266 /* Figure out how much we need to allocate. */
266 /* Figure out how much we need to allocate. */
267 for (nbytes = 40, pos = 0; PyDict_Next(map, &pos, &k, &v);) {
267 for (nbytes = 40, pos = 0; PyDict_Next(map, &pos, &k, &v);) {
268 PyObject *c;
268 PyObject *c;
269 if (!PyString_Check(k)) {
269 if (!PyString_Check(k)) {
270 PyErr_SetString(PyExc_TypeError, "expected string key");
270 PyErr_SetString(PyExc_TypeError, "expected string key");
271 goto bail;
271 goto bail;
272 }
272 }
273 nbytes += PyString_GET_SIZE(k) + 17;
273 nbytes += PyString_GET_SIZE(k) + 17;
274 c = PyDict_GetItem(copymap, k);
274 c = PyDict_GetItem(copymap, k);
275 if (c) {
275 if (c) {
276 if (!PyString_Check(c)) {
276 if (!PyString_Check(c)) {
277 PyErr_SetString(PyExc_TypeError,
277 PyErr_SetString(PyExc_TypeError,
278 "expected string key");
278 "expected string key");
279 goto bail;
279 goto bail;
280 }
280 }
281 nbytes += PyString_GET_SIZE(c) + 1;
281 nbytes += PyString_GET_SIZE(c) + 1;
282 }
282 }
283 }
283 }
284
284
285 packobj = PyString_FromStringAndSize(NULL, nbytes);
285 packobj = PyString_FromStringAndSize(NULL, nbytes);
286 if (packobj == NULL)
286 if (packobj == NULL)
287 goto bail;
287 goto bail;
288
288
289 p = PyString_AS_STRING(packobj);
289 p = PyString_AS_STRING(packobj);
290
290
291 pn = PySequence_ITEM(pl, 0);
291 pn = PySequence_ITEM(pl, 0);
292 if (PyString_AsStringAndSize(pn, &s, &l) == -1 || l != 20) {
292 if (PyString_AsStringAndSize(pn, &s, &l) == -1 || l != 20) {
293 PyErr_SetString(PyExc_TypeError, "expected a 20-byte hash");
293 PyErr_SetString(PyExc_TypeError, "expected a 20-byte hash");
294 goto bail;
294 goto bail;
295 }
295 }
296 memcpy(p, s, l);
296 memcpy(p, s, l);
297 p += 20;
297 p += 20;
298 pn = PySequence_ITEM(pl, 1);
298 pn = PySequence_ITEM(pl, 1);
299 if (PyString_AsStringAndSize(pn, &s, &l) == -1 || l != 20) {
299 if (PyString_AsStringAndSize(pn, &s, &l) == -1 || l != 20) {
300 PyErr_SetString(PyExc_TypeError, "expected a 20-byte hash");
300 PyErr_SetString(PyExc_TypeError, "expected a 20-byte hash");
301 goto bail;
301 goto bail;
302 }
302 }
303 memcpy(p, s, l);
303 memcpy(p, s, l);
304 p += 20;
304 p += 20;
305
305
306 for (pos = 0; PyDict_Next(map, &pos, &k, &v); ) {
306 for (pos = 0; PyDict_Next(map, &pos, &k, &v); ) {
307 uint32_t mode, size, mtime;
307 uint32_t mode, size, mtime;
308 Py_ssize_t len, l;
308 Py_ssize_t len, l;
309 PyObject *o;
309 PyObject *o;
310 char *s, *t;
310 char *s, *t;
311
311
312 if (!PyTuple_Check(v) || PyTuple_GET_SIZE(v) != 4) {
312 if (!PyTuple_Check(v) || PyTuple_GET_SIZE(v) != 4) {
313 PyErr_SetString(PyExc_TypeError, "expected a 4-tuple");
313 PyErr_SetString(PyExc_TypeError, "expected a 4-tuple");
314 goto bail;
314 goto bail;
315 }
315 }
316 o = PyTuple_GET_ITEM(v, 0);
316 o = PyTuple_GET_ITEM(v, 0);
317 if (PyString_AsStringAndSize(o, &s, &l) == -1 || l != 1) {
317 if (PyString_AsStringAndSize(o, &s, &l) == -1 || l != 1) {
318 PyErr_SetString(PyExc_TypeError, "expected one byte");
318 PyErr_SetString(PyExc_TypeError, "expected one byte");
319 goto bail;
319 goto bail;
320 }
320 }
321 *p++ = *s;
321 *p++ = *s;
322 if (getintat(v, 1, &mode) == -1)
322 if (getintat(v, 1, &mode) == -1)
323 goto bail;
323 goto bail;
324 if (getintat(v, 2, &size) == -1)
324 if (getintat(v, 2, &size) == -1)
325 goto bail;
325 goto bail;
326 if (getintat(v, 3, &mtime) == -1)
326 if (getintat(v, 3, &mtime) == -1)
327 goto bail;
327 goto bail;
328 if (*s == 'n' && mtime == (uint32_t)now) {
328 if (*s == 'n' && mtime == (uint32_t)now) {
329 /* See dirstate.py:write for why we do this. */
329 /* See pure/parsers.py:pack_dirstate for why we do
330 * this. */
330 if (PyDict_SetItem(map, k, dirstate_unset) == -1)
331 if (PyDict_SetItem(map, k, dirstate_unset) == -1)
331 goto bail;
332 goto bail;
332 mode = 0, size = -1, mtime = -1;
333 mode = 0, size = -1, mtime = -1;
333 }
334 }
334 putbe32(mode, p);
335 putbe32(mode, p);
335 putbe32(size, p + 4);
336 putbe32(size, p + 4);
336 putbe32(mtime, p + 8);
337 putbe32(mtime, p + 8);
337 t = p + 12;
338 t = p + 12;
338 p += 16;
339 p += 16;
339 len = PyString_GET_SIZE(k);
340 len = PyString_GET_SIZE(k);
340 memcpy(p, PyString_AS_STRING(k), len);
341 memcpy(p, PyString_AS_STRING(k), len);
341 p += len;
342 p += len;
342 o = PyDict_GetItem(copymap, k);
343 o = PyDict_GetItem(copymap, k);
343 if (o) {
344 if (o) {
344 *p++ = '\0';
345 *p++ = '\0';
345 l = PyString_GET_SIZE(o);
346 l = PyString_GET_SIZE(o);
346 memcpy(p, PyString_AS_STRING(o), l);
347 memcpy(p, PyString_AS_STRING(o), l);
347 p += l;
348 p += l;
348 len += l + 1;
349 len += l + 1;
349 }
350 }
350 putbe32((uint32_t)len, t);
351 putbe32((uint32_t)len, t);
351 }
352 }
352
353
353 pos = p - PyString_AS_STRING(packobj);
354 pos = p - PyString_AS_STRING(packobj);
354 if (pos != nbytes) {
355 if (pos != nbytes) {
355 PyErr_Format(PyExc_SystemError, "bad dirstate size: %ld != %ld",
356 PyErr_Format(PyExc_SystemError, "bad dirstate size: %ld != %ld",
356 (long)pos, (long)nbytes);
357 (long)pos, (long)nbytes);
357 goto bail;
358 goto bail;
358 }
359 }
359
360
360 return packobj;
361 return packobj;
361 bail:
362 bail:
362 Py_XDECREF(packobj);
363 Py_XDECREF(packobj);
363 return NULL;
364 return NULL;
364 }
365 }
365
366
366 /*
367 /*
367 * A base-16 trie for fast node->rev mapping.
368 * A base-16 trie for fast node->rev mapping.
368 *
369 *
369 * Positive value is index of the next node in the trie
370 * Positive value is index of the next node in the trie
370 * Negative value is a leaf: -(rev + 1)
371 * Negative value is a leaf: -(rev + 1)
371 * Zero is empty
372 * Zero is empty
372 */
373 */
373 typedef struct {
374 typedef struct {
374 int children[16];
375 int children[16];
375 } nodetree;
376 } nodetree;
376
377
377 /*
378 /*
378 * This class has two behaviours.
379 * This class has two behaviours.
379 *
380 *
380 * When used in a list-like way (with integer keys), we decode an
381 * When used in a list-like way (with integer keys), we decode an
381 * entry in a RevlogNG index file on demand. Our last entry is a
382 * entry in a RevlogNG index file on demand. Our last entry is a
382 * sentinel, always a nullid. We have limited support for
383 * sentinel, always a nullid. We have limited support for
383 * integer-keyed insert and delete, only at elements right before the
384 * integer-keyed insert and delete, only at elements right before the
384 * sentinel.
385 * sentinel.
385 *
386 *
386 * With string keys, we lazily perform a reverse mapping from node to
387 * With string keys, we lazily perform a reverse mapping from node to
387 * rev, using a base-16 trie.
388 * rev, using a base-16 trie.
388 */
389 */
389 typedef struct {
390 typedef struct {
390 PyObject_HEAD
391 PyObject_HEAD
391 /* Type-specific fields go here. */
392 /* Type-specific fields go here. */
392 PyObject *data; /* raw bytes of index */
393 PyObject *data; /* raw bytes of index */
393 PyObject **cache; /* cached tuples */
394 PyObject **cache; /* cached tuples */
394 const char **offsets; /* populated on demand */
395 const char **offsets; /* populated on demand */
395 Py_ssize_t raw_length; /* original number of elements */
396 Py_ssize_t raw_length; /* original number of elements */
396 Py_ssize_t length; /* current number of elements */
397 Py_ssize_t length; /* current number of elements */
397 PyObject *added; /* populated on demand */
398 PyObject *added; /* populated on demand */
398 PyObject *headrevs; /* cache, invalidated on changes */
399 PyObject *headrevs; /* cache, invalidated on changes */
399 nodetree *nt; /* base-16 trie */
400 nodetree *nt; /* base-16 trie */
400 int ntlength; /* # nodes in use */
401 int ntlength; /* # nodes in use */
401 int ntcapacity; /* # nodes allocated */
402 int ntcapacity; /* # nodes allocated */
402 int ntdepth; /* maximum depth of tree */
403 int ntdepth; /* maximum depth of tree */
403 int ntsplits; /* # splits performed */
404 int ntsplits; /* # splits performed */
404 int ntrev; /* last rev scanned */
405 int ntrev; /* last rev scanned */
405 int ntlookups; /* # lookups */
406 int ntlookups; /* # lookups */
406 int ntmisses; /* # lookups that miss the cache */
407 int ntmisses; /* # lookups that miss the cache */
407 int inlined;
408 int inlined;
408 } indexObject;
409 } indexObject;
409
410
410 static Py_ssize_t index_length(const indexObject *self)
411 static Py_ssize_t index_length(const indexObject *self)
411 {
412 {
412 if (self->added == NULL)
413 if (self->added == NULL)
413 return self->length;
414 return self->length;
414 return self->length + PyList_GET_SIZE(self->added);
415 return self->length + PyList_GET_SIZE(self->added);
415 }
416 }
416
417
417 static PyObject *nullentry;
418 static PyObject *nullentry;
418 static const char nullid[20];
419 static const char nullid[20];
419
420
420 static long inline_scan(indexObject *self, const char **offsets);
421 static long inline_scan(indexObject *self, const char **offsets);
421
422
422 #if LONG_MAX == 0x7fffffffL
423 #if LONG_MAX == 0x7fffffffL
423 static char *tuple_format = "Kiiiiiis#";
424 static char *tuple_format = "Kiiiiiis#";
424 #else
425 #else
425 static char *tuple_format = "kiiiiiis#";
426 static char *tuple_format = "kiiiiiis#";
426 #endif
427 #endif
427
428
428 /* A RevlogNG v1 index entry is 64 bytes long. */
429 /* A RevlogNG v1 index entry is 64 bytes long. */
429 static const long v1_hdrsize = 64;
430 static const long v1_hdrsize = 64;
430
431
431 /*
432 /*
432 * Return a pointer to the beginning of a RevlogNG record.
433 * Return a pointer to the beginning of a RevlogNG record.
433 */
434 */
434 static const char *index_deref(indexObject *self, Py_ssize_t pos)
435 static const char *index_deref(indexObject *self, Py_ssize_t pos)
435 {
436 {
436 if (self->inlined && pos > 0) {
437 if (self->inlined && pos > 0) {
437 if (self->offsets == NULL) {
438 if (self->offsets == NULL) {
438 self->offsets = malloc(self->raw_length *
439 self->offsets = malloc(self->raw_length *
439 sizeof(*self->offsets));
440 sizeof(*self->offsets));
440 if (self->offsets == NULL)
441 if (self->offsets == NULL)
441 return (const char *)PyErr_NoMemory();
442 return (const char *)PyErr_NoMemory();
442 inline_scan(self, self->offsets);
443 inline_scan(self, self->offsets);
443 }
444 }
444 return self->offsets[pos];
445 return self->offsets[pos];
445 }
446 }
446
447
447 return PyString_AS_STRING(self->data) + pos * v1_hdrsize;
448 return PyString_AS_STRING(self->data) + pos * v1_hdrsize;
448 }
449 }
449
450
450 /*
451 /*
451 * RevlogNG format (all in big endian, data may be inlined):
452 * RevlogNG format (all in big endian, data may be inlined):
452 * 6 bytes: offset
453 * 6 bytes: offset
453 * 2 bytes: flags
454 * 2 bytes: flags
454 * 4 bytes: compressed length
455 * 4 bytes: compressed length
455 * 4 bytes: uncompressed length
456 * 4 bytes: uncompressed length
456 * 4 bytes: base revision
457 * 4 bytes: base revision
457 * 4 bytes: link revision
458 * 4 bytes: link revision
458 * 4 bytes: parent 1 revision
459 * 4 bytes: parent 1 revision
459 * 4 bytes: parent 2 revision
460 * 4 bytes: parent 2 revision
460 * 32 bytes: nodeid (only 20 bytes used)
461 * 32 bytes: nodeid (only 20 bytes used)
461 */
462 */
462 static PyObject *index_get(indexObject *self, Py_ssize_t pos)
463 static PyObject *index_get(indexObject *self, Py_ssize_t pos)
463 {
464 {
464 uint64_t offset_flags;
465 uint64_t offset_flags;
465 int comp_len, uncomp_len, base_rev, link_rev, parent_1, parent_2;
466 int comp_len, uncomp_len, base_rev, link_rev, parent_1, parent_2;
466 const char *c_node_id;
467 const char *c_node_id;
467 const char *data;
468 const char *data;
468 Py_ssize_t length = index_length(self);
469 Py_ssize_t length = index_length(self);
469 PyObject *entry;
470 PyObject *entry;
470
471
471 if (pos < 0)
472 if (pos < 0)
472 pos += length;
473 pos += length;
473
474
474 if (pos < 0 || pos >= length) {
475 if (pos < 0 || pos >= length) {
475 PyErr_SetString(PyExc_IndexError, "revlog index out of range");
476 PyErr_SetString(PyExc_IndexError, "revlog index out of range");
476 return NULL;
477 return NULL;
477 }
478 }
478
479
479 if (pos == length - 1) {
480 if (pos == length - 1) {
480 Py_INCREF(nullentry);
481 Py_INCREF(nullentry);
481 return nullentry;
482 return nullentry;
482 }
483 }
483
484
484 if (pos >= self->length - 1) {
485 if (pos >= self->length - 1) {
485 PyObject *obj;
486 PyObject *obj;
486 obj = PyList_GET_ITEM(self->added, pos - self->length + 1);
487 obj = PyList_GET_ITEM(self->added, pos - self->length + 1);
487 Py_INCREF(obj);
488 Py_INCREF(obj);
488 return obj;
489 return obj;
489 }
490 }
490
491
491 if (self->cache) {
492 if (self->cache) {
492 if (self->cache[pos]) {
493 if (self->cache[pos]) {
493 Py_INCREF(self->cache[pos]);
494 Py_INCREF(self->cache[pos]);
494 return self->cache[pos];
495 return self->cache[pos];
495 }
496 }
496 } else {
497 } else {
497 self->cache = calloc(self->raw_length, sizeof(PyObject *));
498 self->cache = calloc(self->raw_length, sizeof(PyObject *));
498 if (self->cache == NULL)
499 if (self->cache == NULL)
499 return PyErr_NoMemory();
500 return PyErr_NoMemory();
500 }
501 }
501
502
502 data = index_deref(self, pos);
503 data = index_deref(self, pos);
503 if (data == NULL)
504 if (data == NULL)
504 return NULL;
505 return NULL;
505
506
506 offset_flags = getbe32(data + 4);
507 offset_flags = getbe32(data + 4);
507 if (pos == 0) /* mask out version number for the first entry */
508 if (pos == 0) /* mask out version number for the first entry */
508 offset_flags &= 0xFFFF;
509 offset_flags &= 0xFFFF;
509 else {
510 else {
510 uint32_t offset_high = getbe32(data);
511 uint32_t offset_high = getbe32(data);
511 offset_flags |= ((uint64_t)offset_high) << 32;
512 offset_flags |= ((uint64_t)offset_high) << 32;
512 }
513 }
513
514
514 comp_len = getbe32(data + 8);
515 comp_len = getbe32(data + 8);
515 uncomp_len = getbe32(data + 12);
516 uncomp_len = getbe32(data + 12);
516 base_rev = getbe32(data + 16);
517 base_rev = getbe32(data + 16);
517 link_rev = getbe32(data + 20);
518 link_rev = getbe32(data + 20);
518 parent_1 = getbe32(data + 24);
519 parent_1 = getbe32(data + 24);
519 parent_2 = getbe32(data + 28);
520 parent_2 = getbe32(data + 28);
520 c_node_id = data + 32;
521 c_node_id = data + 32;
521
522
522 entry = Py_BuildValue(tuple_format, offset_flags, comp_len,
523 entry = Py_BuildValue(tuple_format, offset_flags, comp_len,
523 uncomp_len, base_rev, link_rev,
524 uncomp_len, base_rev, link_rev,
524 parent_1, parent_2, c_node_id, 20);
525 parent_1, parent_2, c_node_id, 20);
525
526
526 if (entry)
527 if (entry)
527 PyObject_GC_UnTrack(entry);
528 PyObject_GC_UnTrack(entry);
528
529
529 self->cache[pos] = entry;
530 self->cache[pos] = entry;
530 Py_INCREF(entry);
531 Py_INCREF(entry);
531
532
532 return entry;
533 return entry;
533 }
534 }
534
535
535 /*
536 /*
536 * Return the 20-byte SHA of the node corresponding to the given rev.
537 * Return the 20-byte SHA of the node corresponding to the given rev.
537 */
538 */
538 static const char *index_node(indexObject *self, Py_ssize_t pos)
539 static const char *index_node(indexObject *self, Py_ssize_t pos)
539 {
540 {
540 Py_ssize_t length = index_length(self);
541 Py_ssize_t length = index_length(self);
541 const char *data;
542 const char *data;
542
543
543 if (pos == length - 1 || pos == INT_MAX)
544 if (pos == length - 1 || pos == INT_MAX)
544 return nullid;
545 return nullid;
545
546
546 if (pos >= length)
547 if (pos >= length)
547 return NULL;
548 return NULL;
548
549
549 if (pos >= self->length - 1) {
550 if (pos >= self->length - 1) {
550 PyObject *tuple, *str;
551 PyObject *tuple, *str;
551 tuple = PyList_GET_ITEM(self->added, pos - self->length + 1);
552 tuple = PyList_GET_ITEM(self->added, pos - self->length + 1);
552 str = PyTuple_GetItem(tuple, 7);
553 str = PyTuple_GetItem(tuple, 7);
553 return str ? PyString_AS_STRING(str) : NULL;
554 return str ? PyString_AS_STRING(str) : NULL;
554 }
555 }
555
556
556 data = index_deref(self, pos);
557 data = index_deref(self, pos);
557 return data ? data + 32 : NULL;
558 return data ? data + 32 : NULL;
558 }
559 }
559
560
560 static int nt_insert(indexObject *self, const char *node, int rev);
561 static int nt_insert(indexObject *self, const char *node, int rev);
561
562
562 static int node_check(PyObject *obj, char **node, Py_ssize_t *nodelen)
563 static int node_check(PyObject *obj, char **node, Py_ssize_t *nodelen)
563 {
564 {
564 if (PyString_AsStringAndSize(obj, node, nodelen) == -1)
565 if (PyString_AsStringAndSize(obj, node, nodelen) == -1)
565 return -1;
566 return -1;
566 if (*nodelen == 20)
567 if (*nodelen == 20)
567 return 0;
568 return 0;
568 PyErr_SetString(PyExc_ValueError, "20-byte hash required");
569 PyErr_SetString(PyExc_ValueError, "20-byte hash required");
569 return -1;
570 return -1;
570 }
571 }
571
572
572 static PyObject *index_insert(indexObject *self, PyObject *args)
573 static PyObject *index_insert(indexObject *self, PyObject *args)
573 {
574 {
574 PyObject *obj;
575 PyObject *obj;
575 char *node;
576 char *node;
576 long offset;
577 long offset;
577 Py_ssize_t len, nodelen;
578 Py_ssize_t len, nodelen;
578
579
579 if (!PyArg_ParseTuple(args, "lO", &offset, &obj))
580 if (!PyArg_ParseTuple(args, "lO", &offset, &obj))
580 return NULL;
581 return NULL;
581
582
582 if (!PyTuple_Check(obj) || PyTuple_GET_SIZE(obj) != 8) {
583 if (!PyTuple_Check(obj) || PyTuple_GET_SIZE(obj) != 8) {
583 PyErr_SetString(PyExc_TypeError, "8-tuple required");
584 PyErr_SetString(PyExc_TypeError, "8-tuple required");
584 return NULL;
585 return NULL;
585 }
586 }
586
587
587 if (node_check(PyTuple_GET_ITEM(obj, 7), &node, &nodelen) == -1)
588 if (node_check(PyTuple_GET_ITEM(obj, 7), &node, &nodelen) == -1)
588 return NULL;
589 return NULL;
589
590
590 len = index_length(self);
591 len = index_length(self);
591
592
592 if (offset < 0)
593 if (offset < 0)
593 offset += len;
594 offset += len;
594
595
595 if (offset != len - 1) {
596 if (offset != len - 1) {
596 PyErr_SetString(PyExc_IndexError,
597 PyErr_SetString(PyExc_IndexError,
597 "insert only supported at index -1");
598 "insert only supported at index -1");
598 return NULL;
599 return NULL;
599 }
600 }
600
601
601 if (offset > INT_MAX) {
602 if (offset > INT_MAX) {
602 PyErr_SetString(PyExc_ValueError,
603 PyErr_SetString(PyExc_ValueError,
603 "currently only 2**31 revs supported");
604 "currently only 2**31 revs supported");
604 return NULL;
605 return NULL;
605 }
606 }
606
607
607 if (self->added == NULL) {
608 if (self->added == NULL) {
608 self->added = PyList_New(0);
609 self->added = PyList_New(0);
609 if (self->added == NULL)
610 if (self->added == NULL)
610 return NULL;
611 return NULL;
611 }
612 }
612
613
613 if (PyList_Append(self->added, obj) == -1)
614 if (PyList_Append(self->added, obj) == -1)
614 return NULL;
615 return NULL;
615
616
616 if (self->nt)
617 if (self->nt)
617 nt_insert(self, node, (int)offset);
618 nt_insert(self, node, (int)offset);
618
619
619 Py_CLEAR(self->headrevs);
620 Py_CLEAR(self->headrevs);
620 Py_RETURN_NONE;
621 Py_RETURN_NONE;
621 }
622 }
622
623
623 static void _index_clearcaches(indexObject *self)
624 static void _index_clearcaches(indexObject *self)
624 {
625 {
625 if (self->cache) {
626 if (self->cache) {
626 Py_ssize_t i;
627 Py_ssize_t i;
627
628
628 for (i = 0; i < self->raw_length; i++)
629 for (i = 0; i < self->raw_length; i++)
629 Py_CLEAR(self->cache[i]);
630 Py_CLEAR(self->cache[i]);
630 free(self->cache);
631 free(self->cache);
631 self->cache = NULL;
632 self->cache = NULL;
632 }
633 }
633 if (self->offsets) {
634 if (self->offsets) {
634 free(self->offsets);
635 free(self->offsets);
635 self->offsets = NULL;
636 self->offsets = NULL;
636 }
637 }
637 if (self->nt) {
638 if (self->nt) {
638 free(self->nt);
639 free(self->nt);
639 self->nt = NULL;
640 self->nt = NULL;
640 }
641 }
641 Py_CLEAR(self->headrevs);
642 Py_CLEAR(self->headrevs);
642 }
643 }
643
644
644 static PyObject *index_clearcaches(indexObject *self)
645 static PyObject *index_clearcaches(indexObject *self)
645 {
646 {
646 _index_clearcaches(self);
647 _index_clearcaches(self);
647 self->ntlength = self->ntcapacity = 0;
648 self->ntlength = self->ntcapacity = 0;
648 self->ntdepth = self->ntsplits = 0;
649 self->ntdepth = self->ntsplits = 0;
649 self->ntrev = -1;
650 self->ntrev = -1;
650 self->ntlookups = self->ntmisses = 0;
651 self->ntlookups = self->ntmisses = 0;
651 Py_RETURN_NONE;
652 Py_RETURN_NONE;
652 }
653 }
653
654
654 static PyObject *index_stats(indexObject *self)
655 static PyObject *index_stats(indexObject *self)
655 {
656 {
656 PyObject *obj = PyDict_New();
657 PyObject *obj = PyDict_New();
657
658
658 if (obj == NULL)
659 if (obj == NULL)
659 return NULL;
660 return NULL;
660
661
661 #define istat(__n, __d) \
662 #define istat(__n, __d) \
662 if (PyDict_SetItemString(obj, __d, PyInt_FromSsize_t(self->__n)) == -1) \
663 if (PyDict_SetItemString(obj, __d, PyInt_FromSsize_t(self->__n)) == -1) \
663 goto bail;
664 goto bail;
664
665
665 if (self->added) {
666 if (self->added) {
666 Py_ssize_t len = PyList_GET_SIZE(self->added);
667 Py_ssize_t len = PyList_GET_SIZE(self->added);
667 if (PyDict_SetItemString(obj, "index entries added",
668 if (PyDict_SetItemString(obj, "index entries added",
668 PyInt_FromSsize_t(len)) == -1)
669 PyInt_FromSsize_t(len)) == -1)
669 goto bail;
670 goto bail;
670 }
671 }
671
672
672 if (self->raw_length != self->length - 1)
673 if (self->raw_length != self->length - 1)
673 istat(raw_length, "revs on disk");
674 istat(raw_length, "revs on disk");
674 istat(length, "revs in memory");
675 istat(length, "revs in memory");
675 istat(ntcapacity, "node trie capacity");
676 istat(ntcapacity, "node trie capacity");
676 istat(ntdepth, "node trie depth");
677 istat(ntdepth, "node trie depth");
677 istat(ntlength, "node trie count");
678 istat(ntlength, "node trie count");
678 istat(ntlookups, "node trie lookups");
679 istat(ntlookups, "node trie lookups");
679 istat(ntmisses, "node trie misses");
680 istat(ntmisses, "node trie misses");
680 istat(ntrev, "node trie last rev scanned");
681 istat(ntrev, "node trie last rev scanned");
681 istat(ntsplits, "node trie splits");
682 istat(ntsplits, "node trie splits");
682
683
683 #undef istat
684 #undef istat
684
685
685 return obj;
686 return obj;
686
687
687 bail:
688 bail:
688 Py_XDECREF(obj);
689 Py_XDECREF(obj);
689 return NULL;
690 return NULL;
690 }
691 }
691
692
692 /*
693 /*
693 * When we cache a list, we want to be sure the caller can't mutate
694 * When we cache a list, we want to be sure the caller can't mutate
694 * the cached copy.
695 * the cached copy.
695 */
696 */
696 static PyObject *list_copy(PyObject *list)
697 static PyObject *list_copy(PyObject *list)
697 {
698 {
698 Py_ssize_t len = PyList_GET_SIZE(list);
699 Py_ssize_t len = PyList_GET_SIZE(list);
699 PyObject *newlist = PyList_New(len);
700 PyObject *newlist = PyList_New(len);
700 Py_ssize_t i;
701 Py_ssize_t i;
701
702
702 if (newlist == NULL)
703 if (newlist == NULL)
703 return NULL;
704 return NULL;
704
705
705 for (i = 0; i < len; i++) {
706 for (i = 0; i < len; i++) {
706 PyObject *obj = PyList_GET_ITEM(list, i);
707 PyObject *obj = PyList_GET_ITEM(list, i);
707 Py_INCREF(obj);
708 Py_INCREF(obj);
708 PyList_SET_ITEM(newlist, i, obj);
709 PyList_SET_ITEM(newlist, i, obj);
709 }
710 }
710
711
711 return newlist;
712 return newlist;
712 }
713 }
713
714
714 static PyObject *index_headrevs(indexObject *self)
715 static PyObject *index_headrevs(indexObject *self)
715 {
716 {
716 Py_ssize_t i, len, addlen;
717 Py_ssize_t i, len, addlen;
717 char *nothead = NULL;
718 char *nothead = NULL;
718 PyObject *heads;
719 PyObject *heads;
719
720
720 if (self->headrevs)
721 if (self->headrevs)
721 return list_copy(self->headrevs);
722 return list_copy(self->headrevs);
722
723
723 len = index_length(self) - 1;
724 len = index_length(self) - 1;
724 heads = PyList_New(0);
725 heads = PyList_New(0);
725 if (heads == NULL)
726 if (heads == NULL)
726 goto bail;
727 goto bail;
727 if (len == 0) {
728 if (len == 0) {
728 PyObject *nullid = PyInt_FromLong(-1);
729 PyObject *nullid = PyInt_FromLong(-1);
729 if (nullid == NULL || PyList_Append(heads, nullid) == -1) {
730 if (nullid == NULL || PyList_Append(heads, nullid) == -1) {
730 Py_XDECREF(nullid);
731 Py_XDECREF(nullid);
731 goto bail;
732 goto bail;
732 }
733 }
733 goto done;
734 goto done;
734 }
735 }
735
736
736 nothead = calloc(len, 1);
737 nothead = calloc(len, 1);
737 if (nothead == NULL)
738 if (nothead == NULL)
738 goto bail;
739 goto bail;
739
740
740 for (i = 0; i < self->raw_length; i++) {
741 for (i = 0; i < self->raw_length; i++) {
741 const char *data = index_deref(self, i);
742 const char *data = index_deref(self, i);
742 int parent_1 = getbe32(data + 24);
743 int parent_1 = getbe32(data + 24);
743 int parent_2 = getbe32(data + 28);
744 int parent_2 = getbe32(data + 28);
744 if (parent_1 >= 0)
745 if (parent_1 >= 0)
745 nothead[parent_1] = 1;
746 nothead[parent_1] = 1;
746 if (parent_2 >= 0)
747 if (parent_2 >= 0)
747 nothead[parent_2] = 1;
748 nothead[parent_2] = 1;
748 }
749 }
749
750
750 addlen = self->added ? PyList_GET_SIZE(self->added) : 0;
751 addlen = self->added ? PyList_GET_SIZE(self->added) : 0;
751
752
752 for (i = 0; i < addlen; i++) {
753 for (i = 0; i < addlen; i++) {
753 PyObject *rev = PyList_GET_ITEM(self->added, i);
754 PyObject *rev = PyList_GET_ITEM(self->added, i);
754 PyObject *p1 = PyTuple_GET_ITEM(rev, 5);
755 PyObject *p1 = PyTuple_GET_ITEM(rev, 5);
755 PyObject *p2 = PyTuple_GET_ITEM(rev, 6);
756 PyObject *p2 = PyTuple_GET_ITEM(rev, 6);
756 long parent_1, parent_2;
757 long parent_1, parent_2;
757
758
758 if (!PyInt_Check(p1) || !PyInt_Check(p2)) {
759 if (!PyInt_Check(p1) || !PyInt_Check(p2)) {
759 PyErr_SetString(PyExc_TypeError,
760 PyErr_SetString(PyExc_TypeError,
760 "revlog parents are invalid");
761 "revlog parents are invalid");
761 goto bail;
762 goto bail;
762 }
763 }
763 parent_1 = PyInt_AS_LONG(p1);
764 parent_1 = PyInt_AS_LONG(p1);
764 parent_2 = PyInt_AS_LONG(p2);
765 parent_2 = PyInt_AS_LONG(p2);
765 if (parent_1 >= 0)
766 if (parent_1 >= 0)
766 nothead[parent_1] = 1;
767 nothead[parent_1] = 1;
767 if (parent_2 >= 0)
768 if (parent_2 >= 0)
768 nothead[parent_2] = 1;
769 nothead[parent_2] = 1;
769 }
770 }
770
771
771 for (i = 0; i < len; i++) {
772 for (i = 0; i < len; i++) {
772 PyObject *head;
773 PyObject *head;
773
774
774 if (nothead[i])
775 if (nothead[i])
775 continue;
776 continue;
776 head = PyInt_FromLong(i);
777 head = PyInt_FromLong(i);
777 if (head == NULL || PyList_Append(heads, head) == -1) {
778 if (head == NULL || PyList_Append(heads, head) == -1) {
778 Py_XDECREF(head);
779 Py_XDECREF(head);
779 goto bail;
780 goto bail;
780 }
781 }
781 }
782 }
782
783
783 done:
784 done:
784 self->headrevs = heads;
785 self->headrevs = heads;
785 free(nothead);
786 free(nothead);
786 return list_copy(self->headrevs);
787 return list_copy(self->headrevs);
787 bail:
788 bail:
788 Py_XDECREF(heads);
789 Py_XDECREF(heads);
789 free(nothead);
790 free(nothead);
790 return NULL;
791 return NULL;
791 }
792 }
792
793
793 static inline int nt_level(const char *node, Py_ssize_t level)
794 static inline int nt_level(const char *node, Py_ssize_t level)
794 {
795 {
795 int v = node[level>>1];
796 int v = node[level>>1];
796 if (!(level & 1))
797 if (!(level & 1))
797 v >>= 4;
798 v >>= 4;
798 return v & 0xf;
799 return v & 0xf;
799 }
800 }
800
801
801 /*
802 /*
802 * Return values:
803 * Return values:
803 *
804 *
804 * -4: match is ambiguous (multiple candidates)
805 * -4: match is ambiguous (multiple candidates)
805 * -2: not found
806 * -2: not found
806 * rest: valid rev
807 * rest: valid rev
807 */
808 */
808 static int nt_find(indexObject *self, const char *node, Py_ssize_t nodelen,
809 static int nt_find(indexObject *self, const char *node, Py_ssize_t nodelen,
809 int hex)
810 int hex)
810 {
811 {
811 int (*getnybble)(const char *, Py_ssize_t) = hex ? hexdigit : nt_level;
812 int (*getnybble)(const char *, Py_ssize_t) = hex ? hexdigit : nt_level;
812 int level, maxlevel, off;
813 int level, maxlevel, off;
813
814
814 if (nodelen == 20 && node[0] == '\0' && memcmp(node, nullid, 20) == 0)
815 if (nodelen == 20 && node[0] == '\0' && memcmp(node, nullid, 20) == 0)
815 return -1;
816 return -1;
816
817
817 if (self->nt == NULL)
818 if (self->nt == NULL)
818 return -2;
819 return -2;
819
820
820 if (hex)
821 if (hex)
821 maxlevel = nodelen > 40 ? 40 : (int)nodelen;
822 maxlevel = nodelen > 40 ? 40 : (int)nodelen;
822 else
823 else
823 maxlevel = nodelen > 20 ? 40 : ((int)nodelen * 2);
824 maxlevel = nodelen > 20 ? 40 : ((int)nodelen * 2);
824
825
825 for (level = off = 0; level < maxlevel; level++) {
826 for (level = off = 0; level < maxlevel; level++) {
826 int k = getnybble(node, level);
827 int k = getnybble(node, level);
827 nodetree *n = &self->nt[off];
828 nodetree *n = &self->nt[off];
828 int v = n->children[k];
829 int v = n->children[k];
829
830
830 if (v < 0) {
831 if (v < 0) {
831 const char *n;
832 const char *n;
832 Py_ssize_t i;
833 Py_ssize_t i;
833
834
834 v = -v - 1;
835 v = -v - 1;
835 n = index_node(self, v);
836 n = index_node(self, v);
836 if (n == NULL)
837 if (n == NULL)
837 return -2;
838 return -2;
838 for (i = level; i < maxlevel; i++)
839 for (i = level; i < maxlevel; i++)
839 if (getnybble(node, i) != nt_level(n, i))
840 if (getnybble(node, i) != nt_level(n, i))
840 return -2;
841 return -2;
841 return v;
842 return v;
842 }
843 }
843 if (v == 0)
844 if (v == 0)
844 return -2;
845 return -2;
845 off = v;
846 off = v;
846 }
847 }
847 /* multiple matches against an ambiguous prefix */
848 /* multiple matches against an ambiguous prefix */
848 return -4;
849 return -4;
849 }
850 }
850
851
851 static int nt_new(indexObject *self)
852 static int nt_new(indexObject *self)
852 {
853 {
853 if (self->ntlength == self->ntcapacity) {
854 if (self->ntlength == self->ntcapacity) {
854 self->ntcapacity *= 2;
855 self->ntcapacity *= 2;
855 self->nt = realloc(self->nt,
856 self->nt = realloc(self->nt,
856 self->ntcapacity * sizeof(nodetree));
857 self->ntcapacity * sizeof(nodetree));
857 if (self->nt == NULL) {
858 if (self->nt == NULL) {
858 PyErr_SetString(PyExc_MemoryError, "out of memory");
859 PyErr_SetString(PyExc_MemoryError, "out of memory");
859 return -1;
860 return -1;
860 }
861 }
861 memset(&self->nt[self->ntlength], 0,
862 memset(&self->nt[self->ntlength], 0,
862 sizeof(nodetree) * (self->ntcapacity - self->ntlength));
863 sizeof(nodetree) * (self->ntcapacity - self->ntlength));
863 }
864 }
864 return self->ntlength++;
865 return self->ntlength++;
865 }
866 }
866
867
867 static int nt_insert(indexObject *self, const char *node, int rev)
868 static int nt_insert(indexObject *self, const char *node, int rev)
868 {
869 {
869 int level = 0;
870 int level = 0;
870 int off = 0;
871 int off = 0;
871
872
872 while (level < 40) {
873 while (level < 40) {
873 int k = nt_level(node, level);
874 int k = nt_level(node, level);
874 nodetree *n;
875 nodetree *n;
875 int v;
876 int v;
876
877
877 n = &self->nt[off];
878 n = &self->nt[off];
878 v = n->children[k];
879 v = n->children[k];
879
880
880 if (v == 0) {
881 if (v == 0) {
881 n->children[k] = -rev - 1;
882 n->children[k] = -rev - 1;
882 return 0;
883 return 0;
883 }
884 }
884 if (v < 0) {
885 if (v < 0) {
885 const char *oldnode = index_node(self, -v - 1);
886 const char *oldnode = index_node(self, -v - 1);
886 int noff;
887 int noff;
887
888
888 if (!oldnode || !memcmp(oldnode, node, 20)) {
889 if (!oldnode || !memcmp(oldnode, node, 20)) {
889 n->children[k] = -rev - 1;
890 n->children[k] = -rev - 1;
890 return 0;
891 return 0;
891 }
892 }
892 noff = nt_new(self);
893 noff = nt_new(self);
893 if (noff == -1)
894 if (noff == -1)
894 return -1;
895 return -1;
895 /* self->nt may have been changed by realloc */
896 /* self->nt may have been changed by realloc */
896 self->nt[off].children[k] = noff;
897 self->nt[off].children[k] = noff;
897 off = noff;
898 off = noff;
898 n = &self->nt[off];
899 n = &self->nt[off];
899 n->children[nt_level(oldnode, ++level)] = v;
900 n->children[nt_level(oldnode, ++level)] = v;
900 if (level > self->ntdepth)
901 if (level > self->ntdepth)
901 self->ntdepth = level;
902 self->ntdepth = level;
902 self->ntsplits += 1;
903 self->ntsplits += 1;
903 } else {
904 } else {
904 level += 1;
905 level += 1;
905 off = v;
906 off = v;
906 }
907 }
907 }
908 }
908
909
909 return -1;
910 return -1;
910 }
911 }
911
912
912 static int nt_init(indexObject *self)
913 static int nt_init(indexObject *self)
913 {
914 {
914 if (self->nt == NULL) {
915 if (self->nt == NULL) {
915 self->ntcapacity = self->raw_length < 4
916 self->ntcapacity = self->raw_length < 4
916 ? 4 : self->raw_length / 2;
917 ? 4 : self->raw_length / 2;
917 self->nt = calloc(self->ntcapacity, sizeof(nodetree));
918 self->nt = calloc(self->ntcapacity, sizeof(nodetree));
918 if (self->nt == NULL) {
919 if (self->nt == NULL) {
919 PyErr_NoMemory();
920 PyErr_NoMemory();
920 return -1;
921 return -1;
921 }
922 }
922 self->ntlength = 1;
923 self->ntlength = 1;
923 self->ntrev = (int)index_length(self) - 1;
924 self->ntrev = (int)index_length(self) - 1;
924 self->ntlookups = 1;
925 self->ntlookups = 1;
925 self->ntmisses = 0;
926 self->ntmisses = 0;
926 if (nt_insert(self, nullid, INT_MAX) == -1)
927 if (nt_insert(self, nullid, INT_MAX) == -1)
927 return -1;
928 return -1;
928 }
929 }
929 return 0;
930 return 0;
930 }
931 }
931
932
932 /*
933 /*
933 * Return values:
934 * Return values:
934 *
935 *
935 * -3: error (exception set)
936 * -3: error (exception set)
936 * -2: not found (no exception set)
937 * -2: not found (no exception set)
937 * rest: valid rev
938 * rest: valid rev
938 */
939 */
939 static int index_find_node(indexObject *self,
940 static int index_find_node(indexObject *self,
940 const char *node, Py_ssize_t nodelen)
941 const char *node, Py_ssize_t nodelen)
941 {
942 {
942 int rev;
943 int rev;
943
944
944 self->ntlookups++;
945 self->ntlookups++;
945 rev = nt_find(self, node, nodelen, 0);
946 rev = nt_find(self, node, nodelen, 0);
946 if (rev >= -1)
947 if (rev >= -1)
947 return rev;
948 return rev;
948
949
949 if (nt_init(self) == -1)
950 if (nt_init(self) == -1)
950 return -3;
951 return -3;
951
952
952 /*
953 /*
953 * For the first handful of lookups, we scan the entire index,
954 * For the first handful of lookups, we scan the entire index,
954 * and cache only the matching nodes. This optimizes for cases
955 * and cache only the matching nodes. This optimizes for cases
955 * like "hg tip", where only a few nodes are accessed.
956 * like "hg tip", where only a few nodes are accessed.
956 *
957 *
957 * After that, we cache every node we visit, using a single
958 * After that, we cache every node we visit, using a single
958 * scan amortized over multiple lookups. This gives the best
959 * scan amortized over multiple lookups. This gives the best
959 * bulk performance, e.g. for "hg log".
960 * bulk performance, e.g. for "hg log".
960 */
961 */
961 if (self->ntmisses++ < 4) {
962 if (self->ntmisses++ < 4) {
962 for (rev = self->ntrev - 1; rev >= 0; rev--) {
963 for (rev = self->ntrev - 1; rev >= 0; rev--) {
963 const char *n = index_node(self, rev);
964 const char *n = index_node(self, rev);
964 if (n == NULL)
965 if (n == NULL)
965 return -2;
966 return -2;
966 if (memcmp(node, n, nodelen > 20 ? 20 : nodelen) == 0) {
967 if (memcmp(node, n, nodelen > 20 ? 20 : nodelen) == 0) {
967 if (nt_insert(self, n, rev) == -1)
968 if (nt_insert(self, n, rev) == -1)
968 return -3;
969 return -3;
969 break;
970 break;
970 }
971 }
971 }
972 }
972 } else {
973 } else {
973 for (rev = self->ntrev - 1; rev >= 0; rev--) {
974 for (rev = self->ntrev - 1; rev >= 0; rev--) {
974 const char *n = index_node(self, rev);
975 const char *n = index_node(self, rev);
975 if (n == NULL) {
976 if (n == NULL) {
976 self->ntrev = rev + 1;
977 self->ntrev = rev + 1;
977 return -2;
978 return -2;
978 }
979 }
979 if (nt_insert(self, n, rev) == -1) {
980 if (nt_insert(self, n, rev) == -1) {
980 self->ntrev = rev + 1;
981 self->ntrev = rev + 1;
981 return -3;
982 return -3;
982 }
983 }
983 if (memcmp(node, n, nodelen > 20 ? 20 : nodelen) == 0) {
984 if (memcmp(node, n, nodelen > 20 ? 20 : nodelen) == 0) {
984 break;
985 break;
985 }
986 }
986 }
987 }
987 self->ntrev = rev;
988 self->ntrev = rev;
988 }
989 }
989
990
990 if (rev >= 0)
991 if (rev >= 0)
991 return rev;
992 return rev;
992 return -2;
993 return -2;
993 }
994 }
994
995
995 static PyObject *raise_revlog_error(void)
996 static PyObject *raise_revlog_error(void)
996 {
997 {
997 static PyObject *errclass;
998 static PyObject *errclass;
998 PyObject *mod = NULL, *errobj;
999 PyObject *mod = NULL, *errobj;
999
1000
1000 if (errclass == NULL) {
1001 if (errclass == NULL) {
1001 PyObject *dict;
1002 PyObject *dict;
1002
1003
1003 mod = PyImport_ImportModule("mercurial.error");
1004 mod = PyImport_ImportModule("mercurial.error");
1004 if (mod == NULL)
1005 if (mod == NULL)
1005 goto classfail;
1006 goto classfail;
1006
1007
1007 dict = PyModule_GetDict(mod);
1008 dict = PyModule_GetDict(mod);
1008 if (dict == NULL)
1009 if (dict == NULL)
1009 goto classfail;
1010 goto classfail;
1010
1011
1011 errclass = PyDict_GetItemString(dict, "RevlogError");
1012 errclass = PyDict_GetItemString(dict, "RevlogError");
1012 if (errclass == NULL) {
1013 if (errclass == NULL) {
1013 PyErr_SetString(PyExc_SystemError,
1014 PyErr_SetString(PyExc_SystemError,
1014 "could not find RevlogError");
1015 "could not find RevlogError");
1015 goto classfail;
1016 goto classfail;
1016 }
1017 }
1017 Py_INCREF(errclass);
1018 Py_INCREF(errclass);
1018 }
1019 }
1019
1020
1020 errobj = PyObject_CallFunction(errclass, NULL);
1021 errobj = PyObject_CallFunction(errclass, NULL);
1021 if (errobj == NULL)
1022 if (errobj == NULL)
1022 return NULL;
1023 return NULL;
1023 PyErr_SetObject(errclass, errobj);
1024 PyErr_SetObject(errclass, errobj);
1024 return errobj;
1025 return errobj;
1025
1026
1026 classfail:
1027 classfail:
1027 Py_XDECREF(mod);
1028 Py_XDECREF(mod);
1028 return NULL;
1029 return NULL;
1029 }
1030 }
1030
1031
1031 static PyObject *index_getitem(indexObject *self, PyObject *value)
1032 static PyObject *index_getitem(indexObject *self, PyObject *value)
1032 {
1033 {
1033 char *node;
1034 char *node;
1034 Py_ssize_t nodelen;
1035 Py_ssize_t nodelen;
1035 int rev;
1036 int rev;
1036
1037
1037 if (PyInt_Check(value))
1038 if (PyInt_Check(value))
1038 return index_get(self, PyInt_AS_LONG(value));
1039 return index_get(self, PyInt_AS_LONG(value));
1039
1040
1040 if (node_check(value, &node, &nodelen) == -1)
1041 if (node_check(value, &node, &nodelen) == -1)
1041 return NULL;
1042 return NULL;
1042 rev = index_find_node(self, node, nodelen);
1043 rev = index_find_node(self, node, nodelen);
1043 if (rev >= -1)
1044 if (rev >= -1)
1044 return PyInt_FromLong(rev);
1045 return PyInt_FromLong(rev);
1045 if (rev == -2)
1046 if (rev == -2)
1046 raise_revlog_error();
1047 raise_revlog_error();
1047 return NULL;
1048 return NULL;
1048 }
1049 }
1049
1050
1050 static int nt_partialmatch(indexObject *self, const char *node,
1051 static int nt_partialmatch(indexObject *self, const char *node,
1051 Py_ssize_t nodelen)
1052 Py_ssize_t nodelen)
1052 {
1053 {
1053 int rev;
1054 int rev;
1054
1055
1055 if (nt_init(self) == -1)
1056 if (nt_init(self) == -1)
1056 return -3;
1057 return -3;
1057
1058
1058 if (self->ntrev > 0) {
1059 if (self->ntrev > 0) {
1059 /* ensure that the radix tree is fully populated */
1060 /* ensure that the radix tree is fully populated */
1060 for (rev = self->ntrev - 1; rev >= 0; rev--) {
1061 for (rev = self->ntrev - 1; rev >= 0; rev--) {
1061 const char *n = index_node(self, rev);
1062 const char *n = index_node(self, rev);
1062 if (n == NULL)
1063 if (n == NULL)
1063 return -2;
1064 return -2;
1064 if (nt_insert(self, n, rev) == -1)
1065 if (nt_insert(self, n, rev) == -1)
1065 return -3;
1066 return -3;
1066 }
1067 }
1067 self->ntrev = rev;
1068 self->ntrev = rev;
1068 }
1069 }
1069
1070
1070 return nt_find(self, node, nodelen, 1);
1071 return nt_find(self, node, nodelen, 1);
1071 }
1072 }
1072
1073
1073 static PyObject *index_partialmatch(indexObject *self, PyObject *args)
1074 static PyObject *index_partialmatch(indexObject *self, PyObject *args)
1074 {
1075 {
1075 const char *fullnode;
1076 const char *fullnode;
1076 int nodelen;
1077 int nodelen;
1077 char *node;
1078 char *node;
1078 int rev, i;
1079 int rev, i;
1079
1080
1080 if (!PyArg_ParseTuple(args, "s#", &node, &nodelen))
1081 if (!PyArg_ParseTuple(args, "s#", &node, &nodelen))
1081 return NULL;
1082 return NULL;
1082
1083
1083 if (nodelen < 4) {
1084 if (nodelen < 4) {
1084 PyErr_SetString(PyExc_ValueError, "key too short");
1085 PyErr_SetString(PyExc_ValueError, "key too short");
1085 return NULL;
1086 return NULL;
1086 }
1087 }
1087
1088
1088 if (nodelen > 40) {
1089 if (nodelen > 40) {
1089 PyErr_SetString(PyExc_ValueError, "key too long");
1090 PyErr_SetString(PyExc_ValueError, "key too long");
1090 return NULL;
1091 return NULL;
1091 }
1092 }
1092
1093
1093 for (i = 0; i < nodelen; i++)
1094 for (i = 0; i < nodelen; i++)
1094 hexdigit(node, i);
1095 hexdigit(node, i);
1095 if (PyErr_Occurred()) {
1096 if (PyErr_Occurred()) {
1096 /* input contains non-hex characters */
1097 /* input contains non-hex characters */
1097 PyErr_Clear();
1098 PyErr_Clear();
1098 Py_RETURN_NONE;
1099 Py_RETURN_NONE;
1099 }
1100 }
1100
1101
1101 rev = nt_partialmatch(self, node, nodelen);
1102 rev = nt_partialmatch(self, node, nodelen);
1102
1103
1103 switch (rev) {
1104 switch (rev) {
1104 case -4:
1105 case -4:
1105 raise_revlog_error();
1106 raise_revlog_error();
1106 case -3:
1107 case -3:
1107 return NULL;
1108 return NULL;
1108 case -2:
1109 case -2:
1109 Py_RETURN_NONE;
1110 Py_RETURN_NONE;
1110 case -1:
1111 case -1:
1111 return PyString_FromStringAndSize(nullid, 20);
1112 return PyString_FromStringAndSize(nullid, 20);
1112 }
1113 }
1113
1114
1114 fullnode = index_node(self, rev);
1115 fullnode = index_node(self, rev);
1115 if (fullnode == NULL) {
1116 if (fullnode == NULL) {
1116 PyErr_Format(PyExc_IndexError,
1117 PyErr_Format(PyExc_IndexError,
1117 "could not access rev %d", rev);
1118 "could not access rev %d", rev);
1118 return NULL;
1119 return NULL;
1119 }
1120 }
1120 return PyString_FromStringAndSize(fullnode, 20);
1121 return PyString_FromStringAndSize(fullnode, 20);
1121 }
1122 }
1122
1123
1123 static PyObject *index_m_get(indexObject *self, PyObject *args)
1124 static PyObject *index_m_get(indexObject *self, PyObject *args)
1124 {
1125 {
1125 Py_ssize_t nodelen;
1126 Py_ssize_t nodelen;
1126 PyObject *val;
1127 PyObject *val;
1127 char *node;
1128 char *node;
1128 int rev;
1129 int rev;
1129
1130
1130 if (!PyArg_ParseTuple(args, "O", &val))
1131 if (!PyArg_ParseTuple(args, "O", &val))
1131 return NULL;
1132 return NULL;
1132 if (node_check(val, &node, &nodelen) == -1)
1133 if (node_check(val, &node, &nodelen) == -1)
1133 return NULL;
1134 return NULL;
1134 rev = index_find_node(self, node, nodelen);
1135 rev = index_find_node(self, node, nodelen);
1135 if (rev == -3)
1136 if (rev == -3)
1136 return NULL;
1137 return NULL;
1137 if (rev == -2)
1138 if (rev == -2)
1138 Py_RETURN_NONE;
1139 Py_RETURN_NONE;
1139 return PyInt_FromLong(rev);
1140 return PyInt_FromLong(rev);
1140 }
1141 }
1141
1142
1142 static int index_contains(indexObject *self, PyObject *value)
1143 static int index_contains(indexObject *self, PyObject *value)
1143 {
1144 {
1144 char *node;
1145 char *node;
1145 Py_ssize_t nodelen;
1146 Py_ssize_t nodelen;
1146
1147
1147 if (PyInt_Check(value)) {
1148 if (PyInt_Check(value)) {
1148 long rev = PyInt_AS_LONG(value);
1149 long rev = PyInt_AS_LONG(value);
1149 return rev >= -1 && rev < index_length(self);
1150 return rev >= -1 && rev < index_length(self);
1150 }
1151 }
1151
1152
1152 if (node_check(value, &node, &nodelen) == -1)
1153 if (node_check(value, &node, &nodelen) == -1)
1153 return -1;
1154 return -1;
1154
1155
1155 switch (index_find_node(self, node, nodelen)) {
1156 switch (index_find_node(self, node, nodelen)) {
1156 case -3:
1157 case -3:
1157 return -1;
1158 return -1;
1158 case -2:
1159 case -2:
1159 return 0;
1160 return 0;
1160 default:
1161 default:
1161 return 1;
1162 return 1;
1162 }
1163 }
1163 }
1164 }
1164
1165
1165 /*
1166 /*
1166 * Invalidate any trie entries introduced by added revs.
1167 * Invalidate any trie entries introduced by added revs.
1167 */
1168 */
1168 static void nt_invalidate_added(indexObject *self, Py_ssize_t start)
1169 static void nt_invalidate_added(indexObject *self, Py_ssize_t start)
1169 {
1170 {
1170 Py_ssize_t i, len = PyList_GET_SIZE(self->added);
1171 Py_ssize_t i, len = PyList_GET_SIZE(self->added);
1171
1172
1172 for (i = start; i < len; i++) {
1173 for (i = start; i < len; i++) {
1173 PyObject *tuple = PyList_GET_ITEM(self->added, i);
1174 PyObject *tuple = PyList_GET_ITEM(self->added, i);
1174 PyObject *node = PyTuple_GET_ITEM(tuple, 7);
1175 PyObject *node = PyTuple_GET_ITEM(tuple, 7);
1175
1176
1176 nt_insert(self, PyString_AS_STRING(node), -1);
1177 nt_insert(self, PyString_AS_STRING(node), -1);
1177 }
1178 }
1178
1179
1179 if (start == 0)
1180 if (start == 0)
1180 Py_CLEAR(self->added);
1181 Py_CLEAR(self->added);
1181 }
1182 }
1182
1183
1183 /*
1184 /*
1184 * Delete a numeric range of revs, which must be at the end of the
1185 * Delete a numeric range of revs, which must be at the end of the
1185 * range, but exclude the sentinel nullid entry.
1186 * range, but exclude the sentinel nullid entry.
1186 */
1187 */
1187 static int index_slice_del(indexObject *self, PyObject *item)
1188 static int index_slice_del(indexObject *self, PyObject *item)
1188 {
1189 {
1189 Py_ssize_t start, stop, step, slicelength;
1190 Py_ssize_t start, stop, step, slicelength;
1190 Py_ssize_t length = index_length(self);
1191 Py_ssize_t length = index_length(self);
1191 int ret = 0;
1192 int ret = 0;
1192
1193
1193 if (PySlice_GetIndicesEx((PySliceObject*)item, length,
1194 if (PySlice_GetIndicesEx((PySliceObject*)item, length,
1194 &start, &stop, &step, &slicelength) < 0)
1195 &start, &stop, &step, &slicelength) < 0)
1195 return -1;
1196 return -1;
1196
1197
1197 if (slicelength <= 0)
1198 if (slicelength <= 0)
1198 return 0;
1199 return 0;
1199
1200
1200 if ((step < 0 && start < stop) || (step > 0 && start > stop))
1201 if ((step < 0 && start < stop) || (step > 0 && start > stop))
1201 stop = start;
1202 stop = start;
1202
1203
1203 if (step < 0) {
1204 if (step < 0) {
1204 stop = start + 1;
1205 stop = start + 1;
1205 start = stop + step*(slicelength - 1) - 1;
1206 start = stop + step*(slicelength - 1) - 1;
1206 step = -step;
1207 step = -step;
1207 }
1208 }
1208
1209
1209 if (step != 1) {
1210 if (step != 1) {
1210 PyErr_SetString(PyExc_ValueError,
1211 PyErr_SetString(PyExc_ValueError,
1211 "revlog index delete requires step size of 1");
1212 "revlog index delete requires step size of 1");
1212 return -1;
1213 return -1;
1213 }
1214 }
1214
1215
1215 if (stop != length - 1) {
1216 if (stop != length - 1) {
1216 PyErr_SetString(PyExc_IndexError,
1217 PyErr_SetString(PyExc_IndexError,
1217 "revlog index deletion indices are invalid");
1218 "revlog index deletion indices are invalid");
1218 return -1;
1219 return -1;
1219 }
1220 }
1220
1221
1221 if (start < self->length - 1) {
1222 if (start < self->length - 1) {
1222 if (self->nt) {
1223 if (self->nt) {
1223 Py_ssize_t i;
1224 Py_ssize_t i;
1224
1225
1225 for (i = start + 1; i < self->length - 1; i++) {
1226 for (i = start + 1; i < self->length - 1; i++) {
1226 const char *node = index_node(self, i);
1227 const char *node = index_node(self, i);
1227
1228
1228 if (node)
1229 if (node)
1229 nt_insert(self, node, -1);
1230 nt_insert(self, node, -1);
1230 }
1231 }
1231 if (self->added)
1232 if (self->added)
1232 nt_invalidate_added(self, 0);
1233 nt_invalidate_added(self, 0);
1233 if (self->ntrev > start)
1234 if (self->ntrev > start)
1234 self->ntrev = (int)start;
1235 self->ntrev = (int)start;
1235 }
1236 }
1236 self->length = start + 1;
1237 self->length = start + 1;
1237 if (start < self->raw_length) {
1238 if (start < self->raw_length) {
1238 if (self->cache) {
1239 if (self->cache) {
1239 Py_ssize_t i;
1240 Py_ssize_t i;
1240 for (i = start; i < self->raw_length; i++)
1241 for (i = start; i < self->raw_length; i++)
1241 Py_CLEAR(self->cache[i]);
1242 Py_CLEAR(self->cache[i]);
1242 }
1243 }
1243 self->raw_length = start;
1244 self->raw_length = start;
1244 }
1245 }
1245 goto done;
1246 goto done;
1246 }
1247 }
1247
1248
1248 if (self->nt) {
1249 if (self->nt) {
1249 nt_invalidate_added(self, start - self->length + 1);
1250 nt_invalidate_added(self, start - self->length + 1);
1250 if (self->ntrev > start)
1251 if (self->ntrev > start)
1251 self->ntrev = (int)start;
1252 self->ntrev = (int)start;
1252 }
1253 }
1253 if (self->added)
1254 if (self->added)
1254 ret = PyList_SetSlice(self->added, start - self->length + 1,
1255 ret = PyList_SetSlice(self->added, start - self->length + 1,
1255 PyList_GET_SIZE(self->added), NULL);
1256 PyList_GET_SIZE(self->added), NULL);
1256 done:
1257 done:
1257 Py_CLEAR(self->headrevs);
1258 Py_CLEAR(self->headrevs);
1258 return ret;
1259 return ret;
1259 }
1260 }
1260
1261
1261 /*
1262 /*
1262 * Supported ops:
1263 * Supported ops:
1263 *
1264 *
1264 * slice deletion
1265 * slice deletion
1265 * string assignment (extend node->rev mapping)
1266 * string assignment (extend node->rev mapping)
1266 * string deletion (shrink node->rev mapping)
1267 * string deletion (shrink node->rev mapping)
1267 */
1268 */
1268 static int index_assign_subscript(indexObject *self, PyObject *item,
1269 static int index_assign_subscript(indexObject *self, PyObject *item,
1269 PyObject *value)
1270 PyObject *value)
1270 {
1271 {
1271 char *node;
1272 char *node;
1272 Py_ssize_t nodelen;
1273 Py_ssize_t nodelen;
1273 long rev;
1274 long rev;
1274
1275
1275 if (PySlice_Check(item) && value == NULL)
1276 if (PySlice_Check(item) && value == NULL)
1276 return index_slice_del(self, item);
1277 return index_slice_del(self, item);
1277
1278
1278 if (node_check(item, &node, &nodelen) == -1)
1279 if (node_check(item, &node, &nodelen) == -1)
1279 return -1;
1280 return -1;
1280
1281
1281 if (value == NULL)
1282 if (value == NULL)
1282 return self->nt ? nt_insert(self, node, -1) : 0;
1283 return self->nt ? nt_insert(self, node, -1) : 0;
1283 rev = PyInt_AsLong(value);
1284 rev = PyInt_AsLong(value);
1284 if (rev > INT_MAX || rev < 0) {
1285 if (rev > INT_MAX || rev < 0) {
1285 if (!PyErr_Occurred())
1286 if (!PyErr_Occurred())
1286 PyErr_SetString(PyExc_ValueError, "rev out of range");
1287 PyErr_SetString(PyExc_ValueError, "rev out of range");
1287 return -1;
1288 return -1;
1288 }
1289 }
1289 return nt_insert(self, node, (int)rev);
1290 return nt_insert(self, node, (int)rev);
1290 }
1291 }
1291
1292
1292 /*
1293 /*
1293 * Find all RevlogNG entries in an index that has inline data. Update
1294 * Find all RevlogNG entries in an index that has inline data. Update
1294 * the optional "offsets" table with those entries.
1295 * the optional "offsets" table with those entries.
1295 */
1296 */
1296 static long inline_scan(indexObject *self, const char **offsets)
1297 static long inline_scan(indexObject *self, const char **offsets)
1297 {
1298 {
1298 const char *data = PyString_AS_STRING(self->data);
1299 const char *data = PyString_AS_STRING(self->data);
1299 const char *end = data + PyString_GET_SIZE(self->data);
1300 const char *end = data + PyString_GET_SIZE(self->data);
1300 long incr = v1_hdrsize;
1301 long incr = v1_hdrsize;
1301 Py_ssize_t len = 0;
1302 Py_ssize_t len = 0;
1302
1303
1303 while (data + v1_hdrsize <= end) {
1304 while (data + v1_hdrsize <= end) {
1304 uint32_t comp_len;
1305 uint32_t comp_len;
1305 const char *old_data;
1306 const char *old_data;
1306 /* 3rd element of header is length of compressed inline data */
1307 /* 3rd element of header is length of compressed inline data */
1307 comp_len = getbe32(data + 8);
1308 comp_len = getbe32(data + 8);
1308 incr = v1_hdrsize + comp_len;
1309 incr = v1_hdrsize + comp_len;
1309 if (incr < v1_hdrsize)
1310 if (incr < v1_hdrsize)
1310 break;
1311 break;
1311 if (offsets)
1312 if (offsets)
1312 offsets[len] = data;
1313 offsets[len] = data;
1313 len++;
1314 len++;
1314 old_data = data;
1315 old_data = data;
1315 data += incr;
1316 data += incr;
1316 if (data <= old_data)
1317 if (data <= old_data)
1317 break;
1318 break;
1318 }
1319 }
1319
1320
1320 if (data != end && data + v1_hdrsize != end) {
1321 if (data != end && data + v1_hdrsize != end) {
1321 if (!PyErr_Occurred())
1322 if (!PyErr_Occurred())
1322 PyErr_SetString(PyExc_ValueError, "corrupt index file");
1323 PyErr_SetString(PyExc_ValueError, "corrupt index file");
1323 return -1;
1324 return -1;
1324 }
1325 }
1325
1326
1326 return len;
1327 return len;
1327 }
1328 }
1328
1329
1329 static int index_init(indexObject *self, PyObject *args)
1330 static int index_init(indexObject *self, PyObject *args)
1330 {
1331 {
1331 PyObject *data_obj, *inlined_obj;
1332 PyObject *data_obj, *inlined_obj;
1332 Py_ssize_t size;
1333 Py_ssize_t size;
1333
1334
1334 if (!PyArg_ParseTuple(args, "OO", &data_obj, &inlined_obj))
1335 if (!PyArg_ParseTuple(args, "OO", &data_obj, &inlined_obj))
1335 return -1;
1336 return -1;
1336 if (!PyString_Check(data_obj)) {
1337 if (!PyString_Check(data_obj)) {
1337 PyErr_SetString(PyExc_TypeError, "data is not a string");
1338 PyErr_SetString(PyExc_TypeError, "data is not a string");
1338 return -1;
1339 return -1;
1339 }
1340 }
1340 size = PyString_GET_SIZE(data_obj);
1341 size = PyString_GET_SIZE(data_obj);
1341
1342
1342 self->inlined = inlined_obj && PyObject_IsTrue(inlined_obj);
1343 self->inlined = inlined_obj && PyObject_IsTrue(inlined_obj);
1343 self->data = data_obj;
1344 self->data = data_obj;
1344 self->cache = NULL;
1345 self->cache = NULL;
1345
1346
1346 self->added = NULL;
1347 self->added = NULL;
1347 self->headrevs = NULL;
1348 self->headrevs = NULL;
1348 self->offsets = NULL;
1349 self->offsets = NULL;
1349 self->nt = NULL;
1350 self->nt = NULL;
1350 self->ntlength = self->ntcapacity = 0;
1351 self->ntlength = self->ntcapacity = 0;
1351 self->ntdepth = self->ntsplits = 0;
1352 self->ntdepth = self->ntsplits = 0;
1352 self->ntlookups = self->ntmisses = 0;
1353 self->ntlookups = self->ntmisses = 0;
1353 self->ntrev = -1;
1354 self->ntrev = -1;
1354 Py_INCREF(self->data);
1355 Py_INCREF(self->data);
1355
1356
1356 if (self->inlined) {
1357 if (self->inlined) {
1357 long len = inline_scan(self, NULL);
1358 long len = inline_scan(self, NULL);
1358 if (len == -1)
1359 if (len == -1)
1359 goto bail;
1360 goto bail;
1360 self->raw_length = len;
1361 self->raw_length = len;
1361 self->length = len + 1;
1362 self->length = len + 1;
1362 } else {
1363 } else {
1363 if (size % v1_hdrsize) {
1364 if (size % v1_hdrsize) {
1364 PyErr_SetString(PyExc_ValueError, "corrupt index file");
1365 PyErr_SetString(PyExc_ValueError, "corrupt index file");
1365 goto bail;
1366 goto bail;
1366 }
1367 }
1367 self->raw_length = size / v1_hdrsize;
1368 self->raw_length = size / v1_hdrsize;
1368 self->length = self->raw_length + 1;
1369 self->length = self->raw_length + 1;
1369 }
1370 }
1370
1371
1371 return 0;
1372 return 0;
1372 bail:
1373 bail:
1373 return -1;
1374 return -1;
1374 }
1375 }
1375
1376
1376 static PyObject *index_nodemap(indexObject *self)
1377 static PyObject *index_nodemap(indexObject *self)
1377 {
1378 {
1378 Py_INCREF(self);
1379 Py_INCREF(self);
1379 return (PyObject *)self;
1380 return (PyObject *)self;
1380 }
1381 }
1381
1382
1382 static void index_dealloc(indexObject *self)
1383 static void index_dealloc(indexObject *self)
1383 {
1384 {
1384 _index_clearcaches(self);
1385 _index_clearcaches(self);
1385 Py_DECREF(self->data);
1386 Py_DECREF(self->data);
1386 Py_XDECREF(self->added);
1387 Py_XDECREF(self->added);
1387 PyObject_Del(self);
1388 PyObject_Del(self);
1388 }
1389 }
1389
1390
1390 static PySequenceMethods index_sequence_methods = {
1391 static PySequenceMethods index_sequence_methods = {
1391 (lenfunc)index_length, /* sq_length */
1392 (lenfunc)index_length, /* sq_length */
1392 0, /* sq_concat */
1393 0, /* sq_concat */
1393 0, /* sq_repeat */
1394 0, /* sq_repeat */
1394 (ssizeargfunc)index_get, /* sq_item */
1395 (ssizeargfunc)index_get, /* sq_item */
1395 0, /* sq_slice */
1396 0, /* sq_slice */
1396 0, /* sq_ass_item */
1397 0, /* sq_ass_item */
1397 0, /* sq_ass_slice */
1398 0, /* sq_ass_slice */
1398 (objobjproc)index_contains, /* sq_contains */
1399 (objobjproc)index_contains, /* sq_contains */
1399 };
1400 };
1400
1401
1401 static PyMappingMethods index_mapping_methods = {
1402 static PyMappingMethods index_mapping_methods = {
1402 (lenfunc)index_length, /* mp_length */
1403 (lenfunc)index_length, /* mp_length */
1403 (binaryfunc)index_getitem, /* mp_subscript */
1404 (binaryfunc)index_getitem, /* mp_subscript */
1404 (objobjargproc)index_assign_subscript, /* mp_ass_subscript */
1405 (objobjargproc)index_assign_subscript, /* mp_ass_subscript */
1405 };
1406 };
1406
1407
1407 static PyMethodDef index_methods[] = {
1408 static PyMethodDef index_methods[] = {
1408 {"clearcaches", (PyCFunction)index_clearcaches, METH_NOARGS,
1409 {"clearcaches", (PyCFunction)index_clearcaches, METH_NOARGS,
1409 "clear the index caches"},
1410 "clear the index caches"},
1410 {"get", (PyCFunction)index_m_get, METH_VARARGS,
1411 {"get", (PyCFunction)index_m_get, METH_VARARGS,
1411 "get an index entry"},
1412 "get an index entry"},
1412 {"headrevs", (PyCFunction)index_headrevs, METH_NOARGS,
1413 {"headrevs", (PyCFunction)index_headrevs, METH_NOARGS,
1413 "get head revisions"},
1414 "get head revisions"},
1414 {"insert", (PyCFunction)index_insert, METH_VARARGS,
1415 {"insert", (PyCFunction)index_insert, METH_VARARGS,
1415 "insert an index entry"},
1416 "insert an index entry"},
1416 {"partialmatch", (PyCFunction)index_partialmatch, METH_VARARGS,
1417 {"partialmatch", (PyCFunction)index_partialmatch, METH_VARARGS,
1417 "match a potentially ambiguous node ID"},
1418 "match a potentially ambiguous node ID"},
1418 {"stats", (PyCFunction)index_stats, METH_NOARGS,
1419 {"stats", (PyCFunction)index_stats, METH_NOARGS,
1419 "stats for the index"},
1420 "stats for the index"},
1420 {NULL} /* Sentinel */
1421 {NULL} /* Sentinel */
1421 };
1422 };
1422
1423
1423 static PyGetSetDef index_getset[] = {
1424 static PyGetSetDef index_getset[] = {
1424 {"nodemap", (getter)index_nodemap, NULL, "nodemap", NULL},
1425 {"nodemap", (getter)index_nodemap, NULL, "nodemap", NULL},
1425 {NULL} /* Sentinel */
1426 {NULL} /* Sentinel */
1426 };
1427 };
1427
1428
1428 static PyTypeObject indexType = {
1429 static PyTypeObject indexType = {
1429 PyObject_HEAD_INIT(NULL)
1430 PyObject_HEAD_INIT(NULL)
1430 0, /* ob_size */
1431 0, /* ob_size */
1431 "parsers.index", /* tp_name */
1432 "parsers.index", /* tp_name */
1432 sizeof(indexObject), /* tp_basicsize */
1433 sizeof(indexObject), /* tp_basicsize */
1433 0, /* tp_itemsize */
1434 0, /* tp_itemsize */
1434 (destructor)index_dealloc, /* tp_dealloc */
1435 (destructor)index_dealloc, /* tp_dealloc */
1435 0, /* tp_print */
1436 0, /* tp_print */
1436 0, /* tp_getattr */
1437 0, /* tp_getattr */
1437 0, /* tp_setattr */
1438 0, /* tp_setattr */
1438 0, /* tp_compare */
1439 0, /* tp_compare */
1439 0, /* tp_repr */
1440 0, /* tp_repr */
1440 0, /* tp_as_number */
1441 0, /* tp_as_number */
1441 &index_sequence_methods, /* tp_as_sequence */
1442 &index_sequence_methods, /* tp_as_sequence */
1442 &index_mapping_methods, /* tp_as_mapping */
1443 &index_mapping_methods, /* tp_as_mapping */
1443 0, /* tp_hash */
1444 0, /* tp_hash */
1444 0, /* tp_call */
1445 0, /* tp_call */
1445 0, /* tp_str */
1446 0, /* tp_str */
1446 0, /* tp_getattro */
1447 0, /* tp_getattro */
1447 0, /* tp_setattro */
1448 0, /* tp_setattro */
1448 0, /* tp_as_buffer */
1449 0, /* tp_as_buffer */
1449 Py_TPFLAGS_DEFAULT, /* tp_flags */
1450 Py_TPFLAGS_DEFAULT, /* tp_flags */
1450 "revlog index", /* tp_doc */
1451 "revlog index", /* tp_doc */
1451 0, /* tp_traverse */
1452 0, /* tp_traverse */
1452 0, /* tp_clear */
1453 0, /* tp_clear */
1453 0, /* tp_richcompare */
1454 0, /* tp_richcompare */
1454 0, /* tp_weaklistoffset */
1455 0, /* tp_weaklistoffset */
1455 0, /* tp_iter */
1456 0, /* tp_iter */
1456 0, /* tp_iternext */
1457 0, /* tp_iternext */
1457 index_methods, /* tp_methods */
1458 index_methods, /* tp_methods */
1458 0, /* tp_members */
1459 0, /* tp_members */
1459 index_getset, /* tp_getset */
1460 index_getset, /* tp_getset */
1460 0, /* tp_base */
1461 0, /* tp_base */
1461 0, /* tp_dict */
1462 0, /* tp_dict */
1462 0, /* tp_descr_get */
1463 0, /* tp_descr_get */
1463 0, /* tp_descr_set */
1464 0, /* tp_descr_set */
1464 0, /* tp_dictoffset */
1465 0, /* tp_dictoffset */
1465 (initproc)index_init, /* tp_init */
1466 (initproc)index_init, /* tp_init */
1466 0, /* tp_alloc */
1467 0, /* tp_alloc */
1467 };
1468 };
1468
1469
1469 /*
1470 /*
1470 * returns a tuple of the form (index, index, cache) with elements as
1471 * returns a tuple of the form (index, index, cache) with elements as
1471 * follows:
1472 * follows:
1472 *
1473 *
1473 * index: an index object that lazily parses RevlogNG records
1474 * index: an index object that lazily parses RevlogNG records
1474 * cache: if data is inlined, a tuple (index_file_content, 0), else None
1475 * cache: if data is inlined, a tuple (index_file_content, 0), else None
1475 *
1476 *
1476 * added complications are for backwards compatibility
1477 * added complications are for backwards compatibility
1477 */
1478 */
1478 static PyObject *parse_index2(PyObject *self, PyObject *args)
1479 static PyObject *parse_index2(PyObject *self, PyObject *args)
1479 {
1480 {
1480 PyObject *tuple = NULL, *cache = NULL;
1481 PyObject *tuple = NULL, *cache = NULL;
1481 indexObject *idx;
1482 indexObject *idx;
1482 int ret;
1483 int ret;
1483
1484
1484 idx = PyObject_New(indexObject, &indexType);
1485 idx = PyObject_New(indexObject, &indexType);
1485 if (idx == NULL)
1486 if (idx == NULL)
1486 goto bail;
1487 goto bail;
1487
1488
1488 ret = index_init(idx, args);
1489 ret = index_init(idx, args);
1489 if (ret == -1)
1490 if (ret == -1)
1490 goto bail;
1491 goto bail;
1491
1492
1492 if (idx->inlined) {
1493 if (idx->inlined) {
1493 cache = Py_BuildValue("iO", 0, idx->data);
1494 cache = Py_BuildValue("iO", 0, idx->data);
1494 if (cache == NULL)
1495 if (cache == NULL)
1495 goto bail;
1496 goto bail;
1496 } else {
1497 } else {
1497 cache = Py_None;
1498 cache = Py_None;
1498 Py_INCREF(cache);
1499 Py_INCREF(cache);
1499 }
1500 }
1500
1501
1501 tuple = Py_BuildValue("NN", idx, cache);
1502 tuple = Py_BuildValue("NN", idx, cache);
1502 if (!tuple)
1503 if (!tuple)
1503 goto bail;
1504 goto bail;
1504 return tuple;
1505 return tuple;
1505
1506
1506 bail:
1507 bail:
1507 Py_XDECREF(idx);
1508 Py_XDECREF(idx);
1508 Py_XDECREF(cache);
1509 Py_XDECREF(cache);
1509 Py_XDECREF(tuple);
1510 Py_XDECREF(tuple);
1510 return NULL;
1511 return NULL;
1511 }
1512 }
1512
1513
1513 static char parsers_doc[] = "Efficient content parsing.";
1514 static char parsers_doc[] = "Efficient content parsing.";
1514
1515
1515 PyObject *encodedir(PyObject *self, PyObject *args);
1516 PyObject *encodedir(PyObject *self, PyObject *args);
1516 PyObject *pathencode(PyObject *self, PyObject *args);
1517 PyObject *pathencode(PyObject *self, PyObject *args);
1517 PyObject *lowerencode(PyObject *self, PyObject *args);
1518 PyObject *lowerencode(PyObject *self, PyObject *args);
1518
1519
1519 static PyMethodDef methods[] = {
1520 static PyMethodDef methods[] = {
1520 {"pack_dirstate", pack_dirstate, METH_VARARGS, "pack a dirstate\n"},
1521 {"pack_dirstate", pack_dirstate, METH_VARARGS, "pack a dirstate\n"},
1521 {"parse_manifest", parse_manifest, METH_VARARGS, "parse a manifest\n"},
1522 {"parse_manifest", parse_manifest, METH_VARARGS, "parse a manifest\n"},
1522 {"parse_dirstate", parse_dirstate, METH_VARARGS, "parse a dirstate\n"},
1523 {"parse_dirstate", parse_dirstate, METH_VARARGS, "parse a dirstate\n"},
1523 {"parse_index2", parse_index2, METH_VARARGS, "parse a revlog index\n"},
1524 {"parse_index2", parse_index2, METH_VARARGS, "parse a revlog index\n"},
1524 {"encodedir", encodedir, METH_VARARGS, "encodedir a path\n"},
1525 {"encodedir", encodedir, METH_VARARGS, "encodedir a path\n"},
1525 {"pathencode", pathencode, METH_VARARGS, "fncache-encode a path\n"},
1526 {"pathencode", pathencode, METH_VARARGS, "fncache-encode a path\n"},
1526 {"lowerencode", lowerencode, METH_VARARGS, "lower-encode a path\n"},
1527 {"lowerencode", lowerencode, METH_VARARGS, "lower-encode a path\n"},
1527 {NULL, NULL}
1528 {NULL, NULL}
1528 };
1529 };
1529
1530
1530 static void module_init(PyObject *mod)
1531 static void module_init(PyObject *mod)
1531 {
1532 {
1532 indexType.tp_new = PyType_GenericNew;
1533 indexType.tp_new = PyType_GenericNew;
1533 if (PyType_Ready(&indexType) < 0)
1534 if (PyType_Ready(&indexType) < 0)
1534 return;
1535 return;
1535 Py_INCREF(&indexType);
1536 Py_INCREF(&indexType);
1536
1537
1537 PyModule_AddObject(mod, "index", (PyObject *)&indexType);
1538 PyModule_AddObject(mod, "index", (PyObject *)&indexType);
1538
1539
1539 nullentry = Py_BuildValue("iiiiiiis#", 0, 0, 0,
1540 nullentry = Py_BuildValue("iiiiiiis#", 0, 0, 0,
1540 -1, -1, -1, -1, nullid, 20);
1541 -1, -1, -1, -1, nullid, 20);
1541 if (nullentry)
1542 if (nullentry)
1542 PyObject_GC_UnTrack(nullentry);
1543 PyObject_GC_UnTrack(nullentry);
1543
1544
1544 dirstate_unset = Py_BuildValue("ciii", 'n', 0, -1, -1);
1545 dirstate_unset = Py_BuildValue("ciii", 'n', 0, -1, -1);
1545 }
1546 }
1546
1547
1547 #ifdef IS_PY3K
1548 #ifdef IS_PY3K
1548 static struct PyModuleDef parsers_module = {
1549 static struct PyModuleDef parsers_module = {
1549 PyModuleDef_HEAD_INIT,
1550 PyModuleDef_HEAD_INIT,
1550 "parsers",
1551 "parsers",
1551 parsers_doc,
1552 parsers_doc,
1552 -1,
1553 -1,
1553 methods
1554 methods
1554 };
1555 };
1555
1556
1556 PyMODINIT_FUNC PyInit_parsers(void)
1557 PyMODINIT_FUNC PyInit_parsers(void)
1557 {
1558 {
1558 PyObject *mod = PyModule_Create(&parsers_module);
1559 PyObject *mod = PyModule_Create(&parsers_module);
1559 module_init(mod);
1560 module_init(mod);
1560 return mod;
1561 return mod;
1561 }
1562 }
1562 #else
1563 #else
1563 PyMODINIT_FUNC initparsers(void)
1564 PyMODINIT_FUNC initparsers(void)
1564 {
1565 {
1565 PyObject *mod = Py_InitModule3("parsers", methods, parsers_doc);
1566 PyObject *mod = Py_InitModule3("parsers", methods, parsers_doc);
1566 module_init(mod);
1567 module_init(mod);
1567 }
1568 }
1568 #endif
1569 #endif
@@ -1,89 +1,115 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 mercurial.node import bin, nullid
8 from mercurial.node import bin, nullid
9 from mercurial import util
9 from mercurial import util
10 import struct, zlib
10 import struct, zlib, cStringIO
11
11
12 _pack = struct.pack
12 _pack = struct.pack
13 _unpack = struct.unpack
13 _unpack = struct.unpack
14 _compress = zlib.compress
14 _compress = zlib.compress
15 _decompress = zlib.decompress
15 _decompress = zlib.decompress
16 _sha = util.sha1
16 _sha = util.sha1
17
17
18 def parse_manifest(mfdict, fdict, lines):
18 def parse_manifest(mfdict, fdict, lines):
19 for l in lines.splitlines():
19 for l in lines.splitlines():
20 f, n = l.split('\0')
20 f, n = l.split('\0')
21 if len(n) > 40:
21 if len(n) > 40:
22 fdict[f] = n[40:]
22 fdict[f] = n[40:]
23 mfdict[f] = bin(n[:40])
23 mfdict[f] = bin(n[:40])
24 else:
24 else:
25 mfdict[f] = bin(n)
25 mfdict[f] = bin(n)
26
26
27 def parse_index2(data, inline):
27 def parse_index2(data, inline):
28 def gettype(q):
28 def gettype(q):
29 return int(q & 0xFFFF)
29 return int(q & 0xFFFF)
30
30
31 def offset_type(offset, type):
31 def offset_type(offset, type):
32 return long(long(offset) << 16 | type)
32 return long(long(offset) << 16 | type)
33
33
34 indexformatng = ">Qiiiiii20s12x"
34 indexformatng = ">Qiiiiii20s12x"
35
35
36 s = struct.calcsize(indexformatng)
36 s = struct.calcsize(indexformatng)
37 index = []
37 index = []
38 cache = None
38 cache = None
39 off = 0
39 off = 0
40
40
41 l = len(data) - s
41 l = len(data) - s
42 append = index.append
42 append = index.append
43 if inline:
43 if inline:
44 cache = (0, data)
44 cache = (0, data)
45 while off <= l:
45 while off <= l:
46 e = _unpack(indexformatng, data[off:off + s])
46 e = _unpack(indexformatng, data[off:off + s])
47 append(e)
47 append(e)
48 if e[1] < 0:
48 if e[1] < 0:
49 break
49 break
50 off += e[1] + s
50 off += e[1] + s
51 else:
51 else:
52 while off <= l:
52 while off <= l:
53 e = _unpack(indexformatng, data[off:off + s])
53 e = _unpack(indexformatng, data[off:off + s])
54 append(e)
54 append(e)
55 off += s
55 off += s
56
56
57 if off != len(data):
57 if off != len(data):
58 raise ValueError('corrupt index file')
58 raise ValueError('corrupt index file')
59
59
60 if index:
60 if index:
61 e = list(index[0])
61 e = list(index[0])
62 type = gettype(e[0])
62 type = gettype(e[0])
63 e[0] = offset_type(0, type)
63 e[0] = offset_type(0, type)
64 index[0] = tuple(e)
64 index[0] = tuple(e)
65
65
66 # add the magic null revision at -1
66 # add the magic null revision at -1
67 index.append((0, 0, 0, -1, -1, -1, -1, nullid))
67 index.append((0, 0, 0, -1, -1, -1, -1, nullid))
68
68
69 return index, cache
69 return index, cache
70
70
71 def parse_dirstate(dmap, copymap, st):
71 def parse_dirstate(dmap, copymap, st):
72 parents = [st[:20], st[20: 40]]
72 parents = [st[:20], st[20: 40]]
73 # dereference fields so they will be local in loop
73 # dereference fields so they will be local in loop
74 format = ">cllll"
74 format = ">cllll"
75 e_size = struct.calcsize(format)
75 e_size = struct.calcsize(format)
76 pos1 = 40
76 pos1 = 40
77 l = len(st)
77 l = len(st)
78
78
79 # the inner loop
79 # the inner loop
80 while pos1 < l:
80 while pos1 < l:
81 pos2 = pos1 + e_size
81 pos2 = pos1 + e_size
82 e = _unpack(">cllll", st[pos1:pos2]) # a literal here is faster
82 e = _unpack(">cllll", st[pos1:pos2]) # a literal here is faster
83 pos1 = pos2 + e[4]
83 pos1 = pos2 + e[4]
84 f = st[pos2:pos1]
84 f = st[pos2:pos1]
85 if '\0' in f:
85 if '\0' in f:
86 f, c = f.split('\0')
86 f, c = f.split('\0')
87 copymap[f] = c
87 copymap[f] = c
88 dmap[f] = e[:4]
88 dmap[f] = e[:4]
89 return parents
89 return parents
90
91 def pack_dirstate(dmap, copymap, pl, now):
92 now = int(now)
93 cs = cStringIO.StringIO()
94 write = cs.write
95 write("".join(pl))
96 for f, e in dmap.iteritems():
97 if e[0] == 'n' and e[3] == now:
98 # The file was last modified "simultaneously" with the current
99 # write to dirstate (i.e. within the same second for file-
100 # systems with a granularity of 1 sec). This commonly happens
101 # for at least a couple of files on 'update'.
102 # The user could change the file without changing its size
103 # within the same second. Invalidate the file's stat data in
104 # dirstate, forcing future 'status' calls to compare the
105 # contents of the file. This prevents mistakenly treating such
106 # files as clean.
107 e = (e[0], 0, -1, -1) # mark entry as 'unset'
108 dmap[f] = e
109
110 if f in copymap:
111 f = "%s\0%s" % (f, copymap[f])
112 e = _pack(">cllll", e[0], e[1], e[2], e[3], len(f))
113 write(e)
114 write(f)
115 return cs.getvalue()
General Comments 0
You need to be logged in to leave comments. Login now