##// END OF EJS Templates
dirstate: track otherparent files same as nonnormal...
Durham Goode -
r31278:1c97a91a default
parent child Browse files
Show More
@@ -1,1263 +1,1287 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
7
8 from __future__ import absolute_import
8 from __future__ import absolute_import
9
9
10 import collections
10 import collections
11 import errno
11 import errno
12 import os
12 import os
13 import stat
13 import stat
14
14
15 from .i18n import _
15 from .i18n import _
16 from .node import nullid
16 from .node import nullid
17 from . import (
17 from . import (
18 encoding,
18 encoding,
19 error,
19 error,
20 match as matchmod,
20 match as matchmod,
21 osutil,
21 osutil,
22 parsers,
22 parsers,
23 pathutil,
23 pathutil,
24 pycompat,
24 pycompat,
25 scmutil,
25 scmutil,
26 txnutil,
26 txnutil,
27 util,
27 util,
28 )
28 )
29
29
30 propertycache = util.propertycache
30 propertycache = util.propertycache
31 filecache = scmutil.filecache
31 filecache = scmutil.filecache
32 _rangemask = 0x7fffffff
32 _rangemask = 0x7fffffff
33
33
34 dirstatetuple = parsers.dirstatetuple
34 dirstatetuple = parsers.dirstatetuple
35
35
36 class repocache(filecache):
36 class repocache(filecache):
37 """filecache for files in .hg/"""
37 """filecache for files in .hg/"""
38 def join(self, obj, fname):
38 def join(self, obj, fname):
39 return obj._opener.join(fname)
39 return obj._opener.join(fname)
40
40
41 class rootcache(filecache):
41 class rootcache(filecache):
42 """filecache for files in the repository root"""
42 """filecache for files in the repository root"""
43 def join(self, obj, fname):
43 def join(self, obj, fname):
44 return obj._join(fname)
44 return obj._join(fname)
45
45
46 def _getfsnow(vfs):
46 def _getfsnow(vfs):
47 '''Get "now" timestamp on filesystem'''
47 '''Get "now" timestamp on filesystem'''
48 tmpfd, tmpname = vfs.mkstemp()
48 tmpfd, tmpname = vfs.mkstemp()
49 try:
49 try:
50 return os.fstat(tmpfd).st_mtime
50 return os.fstat(tmpfd).st_mtime
51 finally:
51 finally:
52 os.close(tmpfd)
52 os.close(tmpfd)
53 vfs.unlink(tmpname)
53 vfs.unlink(tmpname)
54
54
55 def nonnormalentries(dmap):
55 def nonnormalentries(dmap):
56 '''Compute the nonnormal dirstate entries from the dmap'''
56 '''Compute the nonnormal dirstate entries from the dmap'''
57 try:
57 try:
58 return parsers.nonnormalentries(dmap)
58 return parsers.nonnormalotherparententries(dmap)
59 except AttributeError:
59 except AttributeError:
60 return set(fname for fname, e in dmap.iteritems()
60 nonnorm = set()
61 if e[0] != 'n' or e[3] == -1)
61 otherparent = set()
62 for fname, e in dmap.iteritems():
63 if e[0] != 'n' or e[3] == -1:
64 nonnorm.add(fname)
65 if e[0] == 'n' and e[2] == -2:
66 otherparent.add(fname)
67 return nonnorm, otherparent
62
68
63 class dirstate(object):
69 class dirstate(object):
64
70
65 def __init__(self, opener, ui, root, validate):
71 def __init__(self, opener, ui, root, validate):
66 '''Create a new dirstate object.
72 '''Create a new dirstate object.
67
73
68 opener is an open()-like callable that can be used to open the
74 opener is an open()-like callable that can be used to open the
69 dirstate file; root is the root of the directory tracked by
75 dirstate file; root is the root of the directory tracked by
70 the dirstate.
76 the dirstate.
71 '''
77 '''
72 self._opener = opener
78 self._opener = opener
73 self._validate = validate
79 self._validate = validate
74 self._root = root
80 self._root = root
75 # ntpath.join(root, '') of Python 2.7.9 does not add sep if root is
81 # ntpath.join(root, '') of Python 2.7.9 does not add sep if root is
76 # UNC path pointing to root share (issue4557)
82 # UNC path pointing to root share (issue4557)
77 self._rootdir = pathutil.normasprefix(root)
83 self._rootdir = pathutil.normasprefix(root)
78 # internal config: ui.forcecwd
84 # internal config: ui.forcecwd
79 forcecwd = ui.config('ui', 'forcecwd')
85 forcecwd = ui.config('ui', 'forcecwd')
80 if forcecwd:
86 if forcecwd:
81 self._cwd = forcecwd
87 self._cwd = forcecwd
82 self._dirty = False
88 self._dirty = False
83 self._dirtypl = False
89 self._dirtypl = False
84 self._lastnormaltime = 0
90 self._lastnormaltime = 0
85 self._ui = ui
91 self._ui = ui
86 self._filecache = {}
92 self._filecache = {}
87 self._parentwriters = 0
93 self._parentwriters = 0
88 self._filename = 'dirstate'
94 self._filename = 'dirstate'
89 self._pendingfilename = '%s.pending' % self._filename
95 self._pendingfilename = '%s.pending' % self._filename
90 self._plchangecallbacks = {}
96 self._plchangecallbacks = {}
91 self._origpl = None
97 self._origpl = None
92 self._updatedfiles = set()
98 self._updatedfiles = set()
93
99
94 # for consistent view between _pl() and _read() invocations
100 # for consistent view between _pl() and _read() invocations
95 self._pendingmode = None
101 self._pendingmode = None
96
102
97 def beginparentchange(self):
103 def beginparentchange(self):
98 '''Marks the beginning of a set of changes that involve changing
104 '''Marks the beginning of a set of changes that involve changing
99 the dirstate parents. If there is an exception during this time,
105 the dirstate parents. If there is an exception during this time,
100 the dirstate will not be written when the wlock is released. This
106 the dirstate will not be written when the wlock is released. This
101 prevents writing an incoherent dirstate where the parent doesn't
107 prevents writing an incoherent dirstate where the parent doesn't
102 match the contents.
108 match the contents.
103 '''
109 '''
104 self._parentwriters += 1
110 self._parentwriters += 1
105
111
106 def endparentchange(self):
112 def endparentchange(self):
107 '''Marks the end of a set of changes that involve changing the
113 '''Marks the end of a set of changes that involve changing the
108 dirstate parents. Once all parent changes have been marked done,
114 dirstate parents. Once all parent changes have been marked done,
109 the wlock will be free to write the dirstate on release.
115 the wlock will be free to write the dirstate on release.
110 '''
116 '''
111 if self._parentwriters > 0:
117 if self._parentwriters > 0:
112 self._parentwriters -= 1
118 self._parentwriters -= 1
113
119
114 def pendingparentchange(self):
120 def pendingparentchange(self):
115 '''Returns true if the dirstate is in the middle of a set of changes
121 '''Returns true if the dirstate is in the middle of a set of changes
116 that modify the dirstate parent.
122 that modify the dirstate parent.
117 '''
123 '''
118 return self._parentwriters > 0
124 return self._parentwriters > 0
119
125
120 @propertycache
126 @propertycache
121 def _map(self):
127 def _map(self):
122 '''Return the dirstate contents as a map from filename to
128 '''Return the dirstate contents as a map from filename to
123 (state, mode, size, time).'''
129 (state, mode, size, time).'''
124 self._read()
130 self._read()
125 return self._map
131 return self._map
126
132
127 @propertycache
133 @propertycache
128 def _copymap(self):
134 def _copymap(self):
129 self._read()
135 self._read()
130 return self._copymap
136 return self._copymap
131
137
132 @propertycache
138 @propertycache
133 def _nonnormalset(self):
139 def _nonnormalset(self):
134 return nonnormalentries(self._map)
140 nonnorm, otherparents = nonnormalentries(self._map)
141 self._otherparentset = otherparents
142 return nonnorm
143
144 @propertycache
145 def _otherparentset(self):
146 nonnorm, otherparents = nonnormalentries(self._map)
147 self._nonnormalset = nonnorm
148 return otherparents
135
149
136 @propertycache
150 @propertycache
137 def _filefoldmap(self):
151 def _filefoldmap(self):
138 try:
152 try:
139 makefilefoldmap = parsers.make_file_foldmap
153 makefilefoldmap = parsers.make_file_foldmap
140 except AttributeError:
154 except AttributeError:
141 pass
155 pass
142 else:
156 else:
143 return makefilefoldmap(self._map, util.normcasespec,
157 return makefilefoldmap(self._map, util.normcasespec,
144 util.normcasefallback)
158 util.normcasefallback)
145
159
146 f = {}
160 f = {}
147 normcase = util.normcase
161 normcase = util.normcase
148 for name, s in self._map.iteritems():
162 for name, s in self._map.iteritems():
149 if s[0] != 'r':
163 if s[0] != 'r':
150 f[normcase(name)] = name
164 f[normcase(name)] = name
151 f['.'] = '.' # prevents useless util.fspath() invocation
165 f['.'] = '.' # prevents useless util.fspath() invocation
152 return f
166 return f
153
167
154 @propertycache
168 @propertycache
155 def _dirfoldmap(self):
169 def _dirfoldmap(self):
156 f = {}
170 f = {}
157 normcase = util.normcase
171 normcase = util.normcase
158 for name in self._dirs:
172 for name in self._dirs:
159 f[normcase(name)] = name
173 f[normcase(name)] = name
160 return f
174 return f
161
175
162 @repocache('branch')
176 @repocache('branch')
163 def _branch(self):
177 def _branch(self):
164 try:
178 try:
165 return self._opener.read("branch").strip() or "default"
179 return self._opener.read("branch").strip() or "default"
166 except IOError as inst:
180 except IOError as inst:
167 if inst.errno != errno.ENOENT:
181 if inst.errno != errno.ENOENT:
168 raise
182 raise
169 return "default"
183 return "default"
170
184
171 @propertycache
185 @propertycache
172 def _pl(self):
186 def _pl(self):
173 try:
187 try:
174 fp = self._opendirstatefile()
188 fp = self._opendirstatefile()
175 st = fp.read(40)
189 st = fp.read(40)
176 fp.close()
190 fp.close()
177 l = len(st)
191 l = len(st)
178 if l == 40:
192 if l == 40:
179 return st[:20], st[20:40]
193 return st[:20], st[20:40]
180 elif l > 0 and l < 40:
194 elif l > 0 and l < 40:
181 raise error.Abort(_('working directory state appears damaged!'))
195 raise error.Abort(_('working directory state appears damaged!'))
182 except IOError as err:
196 except IOError as err:
183 if err.errno != errno.ENOENT:
197 if err.errno != errno.ENOENT:
184 raise
198 raise
185 return [nullid, nullid]
199 return [nullid, nullid]
186
200
187 @propertycache
201 @propertycache
188 def _dirs(self):
202 def _dirs(self):
189 return util.dirs(self._map, 'r')
203 return util.dirs(self._map, 'r')
190
204
191 def dirs(self):
205 def dirs(self):
192 return self._dirs
206 return self._dirs
193
207
194 @rootcache('.hgignore')
208 @rootcache('.hgignore')
195 def _ignore(self):
209 def _ignore(self):
196 files = self._ignorefiles()
210 files = self._ignorefiles()
197 if not files:
211 if not files:
198 return util.never
212 return util.never
199
213
200 pats = ['include:%s' % f for f in files]
214 pats = ['include:%s' % f for f in files]
201 return matchmod.match(self._root, '', [], pats, warn=self._ui.warn)
215 return matchmod.match(self._root, '', [], pats, warn=self._ui.warn)
202
216
203 @propertycache
217 @propertycache
204 def _slash(self):
218 def _slash(self):
205 return self._ui.configbool('ui', 'slash') and pycompat.ossep != '/'
219 return self._ui.configbool('ui', 'slash') and pycompat.ossep != '/'
206
220
207 @propertycache
221 @propertycache
208 def _checklink(self):
222 def _checklink(self):
209 return util.checklink(self._root)
223 return util.checklink(self._root)
210
224
211 @propertycache
225 @propertycache
212 def _checkexec(self):
226 def _checkexec(self):
213 return util.checkexec(self._root)
227 return util.checkexec(self._root)
214
228
215 @propertycache
229 @propertycache
216 def _checkcase(self):
230 def _checkcase(self):
217 return not util.fscasesensitive(self._join('.hg'))
231 return not util.fscasesensitive(self._join('.hg'))
218
232
219 def _join(self, f):
233 def _join(self, f):
220 # much faster than os.path.join()
234 # much faster than os.path.join()
221 # it's safe because f is always a relative path
235 # it's safe because f is always a relative path
222 return self._rootdir + f
236 return self._rootdir + f
223
237
224 def flagfunc(self, buildfallback):
238 def flagfunc(self, buildfallback):
225 if self._checklink and self._checkexec:
239 if self._checklink and self._checkexec:
226 def f(x):
240 def f(x):
227 try:
241 try:
228 st = os.lstat(self._join(x))
242 st = os.lstat(self._join(x))
229 if util.statislink(st):
243 if util.statislink(st):
230 return 'l'
244 return 'l'
231 if util.statisexec(st):
245 if util.statisexec(st):
232 return 'x'
246 return 'x'
233 except OSError:
247 except OSError:
234 pass
248 pass
235 return ''
249 return ''
236 return f
250 return f
237
251
238 fallback = buildfallback()
252 fallback = buildfallback()
239 if self._checklink:
253 if self._checklink:
240 def f(x):
254 def f(x):
241 if os.path.islink(self._join(x)):
255 if os.path.islink(self._join(x)):
242 return 'l'
256 return 'l'
243 if 'x' in fallback(x):
257 if 'x' in fallback(x):
244 return 'x'
258 return 'x'
245 return ''
259 return ''
246 return f
260 return f
247 if self._checkexec:
261 if self._checkexec:
248 def f(x):
262 def f(x):
249 if 'l' in fallback(x):
263 if 'l' in fallback(x):
250 return 'l'
264 return 'l'
251 if util.isexec(self._join(x)):
265 if util.isexec(self._join(x)):
252 return 'x'
266 return 'x'
253 return ''
267 return ''
254 return f
268 return f
255 else:
269 else:
256 return fallback
270 return fallback
257
271
258 @propertycache
272 @propertycache
259 def _cwd(self):
273 def _cwd(self):
260 return pycompat.getcwd()
274 return pycompat.getcwd()
261
275
262 def getcwd(self):
276 def getcwd(self):
263 '''Return the path from which a canonical path is calculated.
277 '''Return the path from which a canonical path is calculated.
264
278
265 This path should be used to resolve file patterns or to convert
279 This path should be used to resolve file patterns or to convert
266 canonical paths back to file paths for display. It shouldn't be
280 canonical paths back to file paths for display. It shouldn't be
267 used to get real file paths. Use vfs functions instead.
281 used to get real file paths. Use vfs functions instead.
268 '''
282 '''
269 cwd = self._cwd
283 cwd = self._cwd
270 if cwd == self._root:
284 if cwd == self._root:
271 return ''
285 return ''
272 # self._root ends with a path separator if self._root is '/' or 'C:\'
286 # self._root ends with a path separator if self._root is '/' or 'C:\'
273 rootsep = self._root
287 rootsep = self._root
274 if not util.endswithsep(rootsep):
288 if not util.endswithsep(rootsep):
275 rootsep += pycompat.ossep
289 rootsep += pycompat.ossep
276 if cwd.startswith(rootsep):
290 if cwd.startswith(rootsep):
277 return cwd[len(rootsep):]
291 return cwd[len(rootsep):]
278 else:
292 else:
279 # we're outside the repo. return an absolute path.
293 # we're outside the repo. return an absolute path.
280 return cwd
294 return cwd
281
295
282 def pathto(self, f, cwd=None):
296 def pathto(self, f, cwd=None):
283 if cwd is None:
297 if cwd is None:
284 cwd = self.getcwd()
298 cwd = self.getcwd()
285 path = util.pathto(self._root, cwd, f)
299 path = util.pathto(self._root, cwd, f)
286 if self._slash:
300 if self._slash:
287 return util.pconvert(path)
301 return util.pconvert(path)
288 return path
302 return path
289
303
290 def __getitem__(self, key):
304 def __getitem__(self, key):
291 '''Return the current state of key (a filename) in the dirstate.
305 '''Return the current state of key (a filename) in the dirstate.
292
306
293 States are:
307 States are:
294 n normal
308 n normal
295 m needs merging
309 m needs merging
296 r marked for removal
310 r marked for removal
297 a marked for addition
311 a marked for addition
298 ? not tracked
312 ? not tracked
299 '''
313 '''
300 return self._map.get(key, ("?",))[0]
314 return self._map.get(key, ("?",))[0]
301
315
302 def __contains__(self, key):
316 def __contains__(self, key):
303 return key in self._map
317 return key in self._map
304
318
305 def __iter__(self):
319 def __iter__(self):
306 for x in sorted(self._map):
320 for x in sorted(self._map):
307 yield x
321 yield x
308
322
309 def iteritems(self):
323 def iteritems(self):
310 return self._map.iteritems()
324 return self._map.iteritems()
311
325
312 def parents(self):
326 def parents(self):
313 return [self._validate(p) for p in self._pl]
327 return [self._validate(p) for p in self._pl]
314
328
315 def p1(self):
329 def p1(self):
316 return self._validate(self._pl[0])
330 return self._validate(self._pl[0])
317
331
318 def p2(self):
332 def p2(self):
319 return self._validate(self._pl[1])
333 return self._validate(self._pl[1])
320
334
321 def branch(self):
335 def branch(self):
322 return encoding.tolocal(self._branch)
336 return encoding.tolocal(self._branch)
323
337
324 def setparents(self, p1, p2=nullid):
338 def setparents(self, p1, p2=nullid):
325 """Set dirstate parents to p1 and p2.
339 """Set dirstate parents to p1 and p2.
326
340
327 When moving from two parents to one, 'm' merged entries a
341 When moving from two parents to one, 'm' merged entries a
328 adjusted to normal and previous copy records discarded and
342 adjusted to normal and previous copy records discarded and
329 returned by the call.
343 returned by the call.
330
344
331 See localrepo.setparents()
345 See localrepo.setparents()
332 """
346 """
333 if self._parentwriters == 0:
347 if self._parentwriters == 0:
334 raise ValueError("cannot set dirstate parent without "
348 raise ValueError("cannot set dirstate parent without "
335 "calling dirstate.beginparentchange")
349 "calling dirstate.beginparentchange")
336
350
337 self._dirty = self._dirtypl = True
351 self._dirty = self._dirtypl = True
338 oldp2 = self._pl[1]
352 oldp2 = self._pl[1]
339 if self._origpl is None:
353 if self._origpl is None:
340 self._origpl = self._pl
354 self._origpl = self._pl
341 self._pl = p1, p2
355 self._pl = p1, p2
342 copies = {}
356 copies = {}
343 if oldp2 != nullid and p2 == nullid:
357 if oldp2 != nullid and p2 == nullid:
344 for f, s in self._map.iteritems():
358 candidatefiles = self._nonnormalset.union(self._otherparentset)
359 for f in candidatefiles:
360 s = self._map.get(f)
361 if s is None:
362 continue
363
345 # Discard 'm' markers when moving away from a merge state
364 # Discard 'm' markers when moving away from a merge state
346 if s[0] == 'm':
365 if s[0] == 'm':
347 if f in self._copymap:
366 if f in self._copymap:
348 copies[f] = self._copymap[f]
367 copies[f] = self._copymap[f]
349 self.normallookup(f)
368 self.normallookup(f)
350 # Also fix up otherparent markers
369 # Also fix up otherparent markers
351 elif s[0] == 'n' and s[2] == -2:
370 elif s[0] == 'n' and s[2] == -2:
352 if f in self._copymap:
371 if f in self._copymap:
353 copies[f] = self._copymap[f]
372 copies[f] = self._copymap[f]
354 self.add(f)
373 self.add(f)
355 return copies
374 return copies
356
375
357 def setbranch(self, branch):
376 def setbranch(self, branch):
358 self._branch = encoding.fromlocal(branch)
377 self._branch = encoding.fromlocal(branch)
359 f = self._opener('branch', 'w', atomictemp=True, checkambig=True)
378 f = self._opener('branch', 'w', atomictemp=True, checkambig=True)
360 try:
379 try:
361 f.write(self._branch + '\n')
380 f.write(self._branch + '\n')
362 f.close()
381 f.close()
363
382
364 # make sure filecache has the correct stat info for _branch after
383 # make sure filecache has the correct stat info for _branch after
365 # replacing the underlying file
384 # replacing the underlying file
366 ce = self._filecache['_branch']
385 ce = self._filecache['_branch']
367 if ce:
386 if ce:
368 ce.refresh()
387 ce.refresh()
369 except: # re-raises
388 except: # re-raises
370 f.discard()
389 f.discard()
371 raise
390 raise
372
391
373 def _opendirstatefile(self):
392 def _opendirstatefile(self):
374 fp, mode = txnutil.trypending(self._root, self._opener, self._filename)
393 fp, mode = txnutil.trypending(self._root, self._opener, self._filename)
375 if self._pendingmode is not None and self._pendingmode != mode:
394 if self._pendingmode is not None and self._pendingmode != mode:
376 fp.close()
395 fp.close()
377 raise error.Abort(_('working directory state may be '
396 raise error.Abort(_('working directory state may be '
378 'changed parallelly'))
397 'changed parallelly'))
379 self._pendingmode = mode
398 self._pendingmode = mode
380 return fp
399 return fp
381
400
382 def _read(self):
401 def _read(self):
383 self._map = {}
402 self._map = {}
384 self._copymap = {}
403 self._copymap = {}
385 try:
404 try:
386 fp = self._opendirstatefile()
405 fp = self._opendirstatefile()
387 try:
406 try:
388 st = fp.read()
407 st = fp.read()
389 finally:
408 finally:
390 fp.close()
409 fp.close()
391 except IOError as err:
410 except IOError as err:
392 if err.errno != errno.ENOENT:
411 if err.errno != errno.ENOENT:
393 raise
412 raise
394 return
413 return
395 if not st:
414 if not st:
396 return
415 return
397
416
398 if util.safehasattr(parsers, 'dict_new_presized'):
417 if util.safehasattr(parsers, 'dict_new_presized'):
399 # Make an estimate of the number of files in the dirstate based on
418 # Make an estimate of the number of files in the dirstate based on
400 # its size. From a linear regression on a set of real-world repos,
419 # its size. From a linear regression on a set of real-world repos,
401 # all over 10,000 files, the size of a dirstate entry is 85
420 # all over 10,000 files, the size of a dirstate entry is 85
402 # bytes. The cost of resizing is significantly higher than the cost
421 # bytes. The cost of resizing is significantly higher than the cost
403 # of filling in a larger presized dict, so subtract 20% from the
422 # of filling in a larger presized dict, so subtract 20% from the
404 # size.
423 # size.
405 #
424 #
406 # This heuristic is imperfect in many ways, so in a future dirstate
425 # This heuristic is imperfect in many ways, so in a future dirstate
407 # format update it makes sense to just record the number of entries
426 # format update it makes sense to just record the number of entries
408 # on write.
427 # on write.
409 self._map = parsers.dict_new_presized(len(st) / 71)
428 self._map = parsers.dict_new_presized(len(st) / 71)
410
429
411 # Python's garbage collector triggers a GC each time a certain number
430 # Python's garbage collector triggers a GC each time a certain number
412 # of container objects (the number being defined by
431 # of container objects (the number being defined by
413 # gc.get_threshold()) are allocated. parse_dirstate creates a tuple
432 # gc.get_threshold()) are allocated. parse_dirstate creates a tuple
414 # for each file in the dirstate. The C version then immediately marks
433 # for each file in the dirstate. The C version then immediately marks
415 # them as not to be tracked by the collector. However, this has no
434 # them as not to be tracked by the collector. However, this has no
416 # effect on when GCs are triggered, only on what objects the GC looks
435 # effect on when GCs are triggered, only on what objects the GC looks
417 # into. This means that O(number of files) GCs are unavoidable.
436 # into. This means that O(number of files) GCs are unavoidable.
418 # Depending on when in the process's lifetime the dirstate is parsed,
437 # Depending on when in the process's lifetime the dirstate is parsed,
419 # this can get very expensive. As a workaround, disable GC while
438 # this can get very expensive. As a workaround, disable GC while
420 # parsing the dirstate.
439 # parsing the dirstate.
421 #
440 #
422 # (we cannot decorate the function directly since it is in a C module)
441 # (we cannot decorate the function directly since it is in a C module)
423 parse_dirstate = util.nogc(parsers.parse_dirstate)
442 parse_dirstate = util.nogc(parsers.parse_dirstate)
424 p = parse_dirstate(self._map, self._copymap, st)
443 p = parse_dirstate(self._map, self._copymap, st)
425 if not self._dirtypl:
444 if not self._dirtypl:
426 self._pl = p
445 self._pl = p
427
446
428 def invalidate(self):
447 def invalidate(self):
429 for a in ("_map", "_copymap", "_filefoldmap", "_dirfoldmap", "_branch",
448 for a in ("_map", "_copymap", "_filefoldmap", "_dirfoldmap", "_branch",
430 "_pl", "_dirs", "_ignore", "_nonnormalset"):
449 "_pl", "_dirs", "_ignore", "_nonnormalset",
450 "_otherparentset"):
431 if a in self.__dict__:
451 if a in self.__dict__:
432 delattr(self, a)
452 delattr(self, a)
433 self._lastnormaltime = 0
453 self._lastnormaltime = 0
434 self._dirty = False
454 self._dirty = False
435 self._updatedfiles.clear()
455 self._updatedfiles.clear()
436 self._parentwriters = 0
456 self._parentwriters = 0
437 self._origpl = None
457 self._origpl = None
438
458
439 def copy(self, source, dest):
459 def copy(self, source, dest):
440 """Mark dest as a copy of source. Unmark dest if source is None."""
460 """Mark dest as a copy of source. Unmark dest if source is None."""
441 if source == dest:
461 if source == dest:
442 return
462 return
443 self._dirty = True
463 self._dirty = True
444 if source is not None:
464 if source is not None:
445 self._copymap[dest] = source
465 self._copymap[dest] = source
446 self._updatedfiles.add(source)
466 self._updatedfiles.add(source)
447 self._updatedfiles.add(dest)
467 self._updatedfiles.add(dest)
448 elif dest in self._copymap:
468 elif dest in self._copymap:
449 del self._copymap[dest]
469 del self._copymap[dest]
450 self._updatedfiles.add(dest)
470 self._updatedfiles.add(dest)
451
471
452 def copied(self, file):
472 def copied(self, file):
453 return self._copymap.get(file, None)
473 return self._copymap.get(file, None)
454
474
455 def copies(self):
475 def copies(self):
456 return self._copymap
476 return self._copymap
457
477
458 def _droppath(self, f):
478 def _droppath(self, f):
459 if self[f] not in "?r" and "_dirs" in self.__dict__:
479 if self[f] not in "?r" and "_dirs" in self.__dict__:
460 self._dirs.delpath(f)
480 self._dirs.delpath(f)
461
481
462 if "_filefoldmap" in self.__dict__:
482 if "_filefoldmap" in self.__dict__:
463 normed = util.normcase(f)
483 normed = util.normcase(f)
464 if normed in self._filefoldmap:
484 if normed in self._filefoldmap:
465 del self._filefoldmap[normed]
485 del self._filefoldmap[normed]
466
486
467 self._updatedfiles.add(f)
487 self._updatedfiles.add(f)
468
488
469 def _addpath(self, f, state, mode, size, mtime):
489 def _addpath(self, f, state, mode, size, mtime):
470 oldstate = self[f]
490 oldstate = self[f]
471 if state == 'a' or oldstate == 'r':
491 if state == 'a' or oldstate == 'r':
472 scmutil.checkfilename(f)
492 scmutil.checkfilename(f)
473 if f in self._dirs:
493 if f in self._dirs:
474 raise error.Abort(_('directory %r already in dirstate') % f)
494 raise error.Abort(_('directory %r already in dirstate') % f)
475 # shadows
495 # shadows
476 for d in util.finddirs(f):
496 for d in util.finddirs(f):
477 if d in self._dirs:
497 if d in self._dirs:
478 break
498 break
479 if d in self._map and self[d] != 'r':
499 if d in self._map and self[d] != 'r':
480 raise error.Abort(
500 raise error.Abort(
481 _('file %r in dirstate clashes with %r') % (d, f))
501 _('file %r in dirstate clashes with %r') % (d, f))
482 if oldstate in "?r" and "_dirs" in self.__dict__:
502 if oldstate in "?r" and "_dirs" in self.__dict__:
483 self._dirs.addpath(f)
503 self._dirs.addpath(f)
484 self._dirty = True
504 self._dirty = True
485 self._updatedfiles.add(f)
505 self._updatedfiles.add(f)
486 self._map[f] = dirstatetuple(state, mode, size, mtime)
506 self._map[f] = dirstatetuple(state, mode, size, mtime)
487 if state != 'n' or mtime == -1:
507 if state != 'n' or mtime == -1:
488 self._nonnormalset.add(f)
508 self._nonnormalset.add(f)
509 if size == -2:
510 self._otherparentset.add(f)
489
511
490 def normal(self, f):
512 def normal(self, f):
491 '''Mark a file normal and clean.'''
513 '''Mark a file normal and clean.'''
492 s = os.lstat(self._join(f))
514 s = os.lstat(self._join(f))
493 mtime = s.st_mtime
515 mtime = s.st_mtime
494 self._addpath(f, 'n', s.st_mode,
516 self._addpath(f, 'n', s.st_mode,
495 s.st_size & _rangemask, mtime & _rangemask)
517 s.st_size & _rangemask, mtime & _rangemask)
496 if f in self._copymap:
518 if f in self._copymap:
497 del self._copymap[f]
519 del self._copymap[f]
498 if f in self._nonnormalset:
520 if f in self._nonnormalset:
499 self._nonnormalset.remove(f)
521 self._nonnormalset.remove(f)
500 if mtime > self._lastnormaltime:
522 if mtime > self._lastnormaltime:
501 # Remember the most recent modification timeslot for status(),
523 # Remember the most recent modification timeslot for status(),
502 # to make sure we won't miss future size-preserving file content
524 # to make sure we won't miss future size-preserving file content
503 # modifications that happen within the same timeslot.
525 # modifications that happen within the same timeslot.
504 self._lastnormaltime = mtime
526 self._lastnormaltime = mtime
505
527
506 def normallookup(self, f):
528 def normallookup(self, f):
507 '''Mark a file normal, but possibly dirty.'''
529 '''Mark a file normal, but possibly dirty.'''
508 if self._pl[1] != nullid and f in self._map:
530 if self._pl[1] != nullid and f in self._map:
509 # if there is a merge going on and the file was either
531 # if there is a merge going on and the file was either
510 # in state 'm' (-1) or coming from other parent (-2) before
532 # in state 'm' (-1) or coming from other parent (-2) before
511 # being removed, restore that state.
533 # being removed, restore that state.
512 entry = self._map[f]
534 entry = self._map[f]
513 if entry[0] == 'r' and entry[2] in (-1, -2):
535 if entry[0] == 'r' and entry[2] in (-1, -2):
514 source = self._copymap.get(f)
536 source = self._copymap.get(f)
515 if entry[2] == -1:
537 if entry[2] == -1:
516 self.merge(f)
538 self.merge(f)
517 elif entry[2] == -2:
539 elif entry[2] == -2:
518 self.otherparent(f)
540 self.otherparent(f)
519 if source:
541 if source:
520 self.copy(source, f)
542 self.copy(source, f)
521 return
543 return
522 if entry[0] == 'm' or entry[0] == 'n' and entry[2] == -2:
544 if entry[0] == 'm' or entry[0] == 'n' and entry[2] == -2:
523 return
545 return
524 self._addpath(f, 'n', 0, -1, -1)
546 self._addpath(f, 'n', 0, -1, -1)
525 if f in self._copymap:
547 if f in self._copymap:
526 del self._copymap[f]
548 del self._copymap[f]
527 if f in self._nonnormalset:
549 if f in self._nonnormalset:
528 self._nonnormalset.remove(f)
550 self._nonnormalset.remove(f)
529
551
530 def otherparent(self, f):
552 def otherparent(self, f):
531 '''Mark as coming from the other parent, always dirty.'''
553 '''Mark as coming from the other parent, always dirty.'''
532 if self._pl[1] == nullid:
554 if self._pl[1] == nullid:
533 raise error.Abort(_("setting %r to other parent "
555 raise error.Abort(_("setting %r to other parent "
534 "only allowed in merges") % f)
556 "only allowed in merges") % f)
535 if f in self and self[f] == 'n':
557 if f in self and self[f] == 'n':
536 # merge-like
558 # merge-like
537 self._addpath(f, 'm', 0, -2, -1)
559 self._addpath(f, 'm', 0, -2, -1)
538 else:
560 else:
539 # add-like
561 # add-like
540 self._addpath(f, 'n', 0, -2, -1)
562 self._addpath(f, 'n', 0, -2, -1)
541
563
542 if f in self._copymap:
564 if f in self._copymap:
543 del self._copymap[f]
565 del self._copymap[f]
544
566
545 def add(self, f):
567 def add(self, f):
546 '''Mark a file added.'''
568 '''Mark a file added.'''
547 self._addpath(f, 'a', 0, -1, -1)
569 self._addpath(f, 'a', 0, -1, -1)
548 if f in self._copymap:
570 if f in self._copymap:
549 del self._copymap[f]
571 del self._copymap[f]
550
572
551 def remove(self, f):
573 def remove(self, f):
552 '''Mark a file removed.'''
574 '''Mark a file removed.'''
553 self._dirty = True
575 self._dirty = True
554 self._droppath(f)
576 self._droppath(f)
555 size = 0
577 size = 0
556 if self._pl[1] != nullid and f in self._map:
578 if self._pl[1] != nullid and f in self._map:
557 # backup the previous state
579 # backup the previous state
558 entry = self._map[f]
580 entry = self._map[f]
559 if entry[0] == 'm': # merge
581 if entry[0] == 'm': # merge
560 size = -1
582 size = -1
561 elif entry[0] == 'n' and entry[2] == -2: # other parent
583 elif entry[0] == 'n' and entry[2] == -2: # other parent
562 size = -2
584 size = -2
585 self._otherparentset.add(f)
563 self._map[f] = dirstatetuple('r', 0, size, 0)
586 self._map[f] = dirstatetuple('r', 0, size, 0)
564 self._nonnormalset.add(f)
587 self._nonnormalset.add(f)
565 if size == 0 and f in self._copymap:
588 if size == 0 and f in self._copymap:
566 del self._copymap[f]
589 del self._copymap[f]
567
590
568 def merge(self, f):
591 def merge(self, f):
569 '''Mark a file merged.'''
592 '''Mark a file merged.'''
570 if self._pl[1] == nullid:
593 if self._pl[1] == nullid:
571 return self.normallookup(f)
594 return self.normallookup(f)
572 return self.otherparent(f)
595 return self.otherparent(f)
573
596
574 def drop(self, f):
597 def drop(self, f):
575 '''Drop a file from the dirstate'''
598 '''Drop a file from the dirstate'''
576 if f in self._map:
599 if f in self._map:
577 self._dirty = True
600 self._dirty = True
578 self._droppath(f)
601 self._droppath(f)
579 del self._map[f]
602 del self._map[f]
580 if f in self._nonnormalset:
603 if f in self._nonnormalset:
581 self._nonnormalset.remove(f)
604 self._nonnormalset.remove(f)
582 if f in self._copymap:
605 if f in self._copymap:
583 del self._copymap[f]
606 del self._copymap[f]
584
607
585 def _discoverpath(self, path, normed, ignoremissing, exists, storemap):
608 def _discoverpath(self, path, normed, ignoremissing, exists, storemap):
586 if exists is None:
609 if exists is None:
587 exists = os.path.lexists(os.path.join(self._root, path))
610 exists = os.path.lexists(os.path.join(self._root, path))
588 if not exists:
611 if not exists:
589 # Maybe a path component exists
612 # Maybe a path component exists
590 if not ignoremissing and '/' in path:
613 if not ignoremissing and '/' in path:
591 d, f = path.rsplit('/', 1)
614 d, f = path.rsplit('/', 1)
592 d = self._normalize(d, False, ignoremissing, None)
615 d = self._normalize(d, False, ignoremissing, None)
593 folded = d + "/" + f
616 folded = d + "/" + f
594 else:
617 else:
595 # No path components, preserve original case
618 # No path components, preserve original case
596 folded = path
619 folded = path
597 else:
620 else:
598 # recursively normalize leading directory components
621 # recursively normalize leading directory components
599 # against dirstate
622 # against dirstate
600 if '/' in normed:
623 if '/' in normed:
601 d, f = normed.rsplit('/', 1)
624 d, f = normed.rsplit('/', 1)
602 d = self._normalize(d, False, ignoremissing, True)
625 d = self._normalize(d, False, ignoremissing, True)
603 r = self._root + "/" + d
626 r = self._root + "/" + d
604 folded = d + "/" + util.fspath(f, r)
627 folded = d + "/" + util.fspath(f, r)
605 else:
628 else:
606 folded = util.fspath(normed, self._root)
629 folded = util.fspath(normed, self._root)
607 storemap[normed] = folded
630 storemap[normed] = folded
608
631
609 return folded
632 return folded
610
633
611 def _normalizefile(self, path, isknown, ignoremissing=False, exists=None):
634 def _normalizefile(self, path, isknown, ignoremissing=False, exists=None):
612 normed = util.normcase(path)
635 normed = util.normcase(path)
613 folded = self._filefoldmap.get(normed, None)
636 folded = self._filefoldmap.get(normed, None)
614 if folded is None:
637 if folded is None:
615 if isknown:
638 if isknown:
616 folded = path
639 folded = path
617 else:
640 else:
618 folded = self._discoverpath(path, normed, ignoremissing, exists,
641 folded = self._discoverpath(path, normed, ignoremissing, exists,
619 self._filefoldmap)
642 self._filefoldmap)
620 return folded
643 return folded
621
644
622 def _normalize(self, path, isknown, ignoremissing=False, exists=None):
645 def _normalize(self, path, isknown, ignoremissing=False, exists=None):
623 normed = util.normcase(path)
646 normed = util.normcase(path)
624 folded = self._filefoldmap.get(normed, None)
647 folded = self._filefoldmap.get(normed, None)
625 if folded is None:
648 if folded is None:
626 folded = self._dirfoldmap.get(normed, None)
649 folded = self._dirfoldmap.get(normed, None)
627 if folded is None:
650 if folded is None:
628 if isknown:
651 if isknown:
629 folded = path
652 folded = path
630 else:
653 else:
631 # store discovered result in dirfoldmap so that future
654 # store discovered result in dirfoldmap so that future
632 # normalizefile calls don't start matching directories
655 # normalizefile calls don't start matching directories
633 folded = self._discoverpath(path, normed, ignoremissing, exists,
656 folded = self._discoverpath(path, normed, ignoremissing, exists,
634 self._dirfoldmap)
657 self._dirfoldmap)
635 return folded
658 return folded
636
659
637 def normalize(self, path, isknown=False, ignoremissing=False):
660 def normalize(self, path, isknown=False, ignoremissing=False):
638 '''
661 '''
639 normalize the case of a pathname when on a casefolding filesystem
662 normalize the case of a pathname when on a casefolding filesystem
640
663
641 isknown specifies whether the filename came from walking the
664 isknown specifies whether the filename came from walking the
642 disk, to avoid extra filesystem access.
665 disk, to avoid extra filesystem access.
643
666
644 If ignoremissing is True, missing path are returned
667 If ignoremissing is True, missing path are returned
645 unchanged. Otherwise, we try harder to normalize possibly
668 unchanged. Otherwise, we try harder to normalize possibly
646 existing path components.
669 existing path components.
647
670
648 The normalized case is determined based on the following precedence:
671 The normalized case is determined based on the following precedence:
649
672
650 - version of name already stored in the dirstate
673 - version of name already stored in the dirstate
651 - version of name stored on disk
674 - version of name stored on disk
652 - version provided via command arguments
675 - version provided via command arguments
653 '''
676 '''
654
677
655 if self._checkcase:
678 if self._checkcase:
656 return self._normalize(path, isknown, ignoremissing)
679 return self._normalize(path, isknown, ignoremissing)
657 return path
680 return path
658
681
659 def clear(self):
682 def clear(self):
660 self._map = {}
683 self._map = {}
661 self._nonnormalset = set()
684 self._nonnormalset = set()
685 self._otherparentset = set()
662 if "_dirs" in self.__dict__:
686 if "_dirs" in self.__dict__:
663 delattr(self, "_dirs")
687 delattr(self, "_dirs")
664 self._copymap = {}
688 self._copymap = {}
665 self._pl = [nullid, nullid]
689 self._pl = [nullid, nullid]
666 self._lastnormaltime = 0
690 self._lastnormaltime = 0
667 self._updatedfiles.clear()
691 self._updatedfiles.clear()
668 self._dirty = True
692 self._dirty = True
669
693
670 def rebuild(self, parent, allfiles, changedfiles=None):
694 def rebuild(self, parent, allfiles, changedfiles=None):
671 if changedfiles is None:
695 if changedfiles is None:
672 # Rebuild entire dirstate
696 # Rebuild entire dirstate
673 changedfiles = allfiles
697 changedfiles = allfiles
674 lastnormaltime = self._lastnormaltime
698 lastnormaltime = self._lastnormaltime
675 self.clear()
699 self.clear()
676 self._lastnormaltime = lastnormaltime
700 self._lastnormaltime = lastnormaltime
677
701
678 if self._origpl is None:
702 if self._origpl is None:
679 self._origpl = self._pl
703 self._origpl = self._pl
680 self._pl = (parent, nullid)
704 self._pl = (parent, nullid)
681 for f in changedfiles:
705 for f in changedfiles:
682 if f in allfiles:
706 if f in allfiles:
683 self.normallookup(f)
707 self.normallookup(f)
684 else:
708 else:
685 self.drop(f)
709 self.drop(f)
686
710
687 self._dirty = True
711 self._dirty = True
688
712
689 def write(self, tr):
713 def write(self, tr):
690 if not self._dirty:
714 if not self._dirty:
691 return
715 return
692
716
693 filename = self._filename
717 filename = self._filename
694 if tr:
718 if tr:
695 # 'dirstate.write()' is not only for writing in-memory
719 # 'dirstate.write()' is not only for writing in-memory
696 # changes out, but also for dropping ambiguous timestamp.
720 # changes out, but also for dropping ambiguous timestamp.
697 # delayed writing re-raise "ambiguous timestamp issue".
721 # delayed writing re-raise "ambiguous timestamp issue".
698 # See also the wiki page below for detail:
722 # See also the wiki page below for detail:
699 # https://www.mercurial-scm.org/wiki/DirstateTransactionPlan
723 # https://www.mercurial-scm.org/wiki/DirstateTransactionPlan
700
724
701 # emulate dropping timestamp in 'parsers.pack_dirstate'
725 # emulate dropping timestamp in 'parsers.pack_dirstate'
702 now = _getfsnow(self._opener)
726 now = _getfsnow(self._opener)
703 dmap = self._map
727 dmap = self._map
704 for f in self._updatedfiles:
728 for f in self._updatedfiles:
705 e = dmap.get(f)
729 e = dmap.get(f)
706 if e is not None and e[0] == 'n' and e[3] == now:
730 if e is not None and e[0] == 'n' and e[3] == now:
707 dmap[f] = dirstatetuple(e[0], e[1], e[2], -1)
731 dmap[f] = dirstatetuple(e[0], e[1], e[2], -1)
708 self._nonnormalset.add(f)
732 self._nonnormalset.add(f)
709
733
710 # emulate that all 'dirstate.normal' results are written out
734 # emulate that all 'dirstate.normal' results are written out
711 self._lastnormaltime = 0
735 self._lastnormaltime = 0
712 self._updatedfiles.clear()
736 self._updatedfiles.clear()
713
737
714 # delay writing in-memory changes out
738 # delay writing in-memory changes out
715 tr.addfilegenerator('dirstate', (self._filename,),
739 tr.addfilegenerator('dirstate', (self._filename,),
716 self._writedirstate, location='plain')
740 self._writedirstate, location='plain')
717 return
741 return
718
742
719 st = self._opener(filename, "w", atomictemp=True, checkambig=True)
743 st = self._opener(filename, "w", atomictemp=True, checkambig=True)
720 self._writedirstate(st)
744 self._writedirstate(st)
721
745
722 def addparentchangecallback(self, category, callback):
746 def addparentchangecallback(self, category, callback):
723 """add a callback to be called when the wd parents are changed
747 """add a callback to be called when the wd parents are changed
724
748
725 Callback will be called with the following arguments:
749 Callback will be called with the following arguments:
726 dirstate, (oldp1, oldp2), (newp1, newp2)
750 dirstate, (oldp1, oldp2), (newp1, newp2)
727
751
728 Category is a unique identifier to allow overwriting an old callback
752 Category is a unique identifier to allow overwriting an old callback
729 with a newer callback.
753 with a newer callback.
730 """
754 """
731 self._plchangecallbacks[category] = callback
755 self._plchangecallbacks[category] = callback
732
756
733 def _writedirstate(self, st):
757 def _writedirstate(self, st):
734 # notify callbacks about parents change
758 # notify callbacks about parents change
735 if self._origpl is not None and self._origpl != self._pl:
759 if self._origpl is not None and self._origpl != self._pl:
736 for c, callback in sorted(self._plchangecallbacks.iteritems()):
760 for c, callback in sorted(self._plchangecallbacks.iteritems()):
737 callback(self, self._origpl, self._pl)
761 callback(self, self._origpl, self._pl)
738 self._origpl = None
762 self._origpl = None
739 # use the modification time of the newly created temporary file as the
763 # use the modification time of the newly created temporary file as the
740 # filesystem's notion of 'now'
764 # filesystem's notion of 'now'
741 now = util.fstat(st).st_mtime & _rangemask
765 now = util.fstat(st).st_mtime & _rangemask
742
766
743 # enough 'delaywrite' prevents 'pack_dirstate' from dropping
767 # enough 'delaywrite' prevents 'pack_dirstate' from dropping
744 # timestamp of each entries in dirstate, because of 'now > mtime'
768 # timestamp of each entries in dirstate, because of 'now > mtime'
745 delaywrite = self._ui.configint('debug', 'dirstate.delaywrite', 0)
769 delaywrite = self._ui.configint('debug', 'dirstate.delaywrite', 0)
746 if delaywrite > 0:
770 if delaywrite > 0:
747 # do we have any files to delay for?
771 # do we have any files to delay for?
748 for f, e in self._map.iteritems():
772 for f, e in self._map.iteritems():
749 if e[0] == 'n' and e[3] == now:
773 if e[0] == 'n' and e[3] == now:
750 import time # to avoid useless import
774 import time # to avoid useless import
751 # rather than sleep n seconds, sleep until the next
775 # rather than sleep n seconds, sleep until the next
752 # multiple of n seconds
776 # multiple of n seconds
753 clock = time.time()
777 clock = time.time()
754 start = int(clock) - (int(clock) % delaywrite)
778 start = int(clock) - (int(clock) % delaywrite)
755 end = start + delaywrite
779 end = start + delaywrite
756 time.sleep(end - clock)
780 time.sleep(end - clock)
757 now = end # trust our estimate that the end is near now
781 now = end # trust our estimate that the end is near now
758 break
782 break
759
783
760 st.write(parsers.pack_dirstate(self._map, self._copymap, self._pl, now))
784 st.write(parsers.pack_dirstate(self._map, self._copymap, self._pl, now))
761 self._nonnormalset = nonnormalentries(self._map)
785 self._nonnormalset, self._otherparentset = nonnormalentries(self._map)
762 st.close()
786 st.close()
763 self._lastnormaltime = 0
787 self._lastnormaltime = 0
764 self._dirty = self._dirtypl = False
788 self._dirty = self._dirtypl = False
765
789
766 def _dirignore(self, f):
790 def _dirignore(self, f):
767 if f == '.':
791 if f == '.':
768 return False
792 return False
769 if self._ignore(f):
793 if self._ignore(f):
770 return True
794 return True
771 for p in util.finddirs(f):
795 for p in util.finddirs(f):
772 if self._ignore(p):
796 if self._ignore(p):
773 return True
797 return True
774 return False
798 return False
775
799
776 def _ignorefiles(self):
800 def _ignorefiles(self):
777 files = []
801 files = []
778 if os.path.exists(self._join('.hgignore')):
802 if os.path.exists(self._join('.hgignore')):
779 files.append(self._join('.hgignore'))
803 files.append(self._join('.hgignore'))
780 for name, path in self._ui.configitems("ui"):
804 for name, path in self._ui.configitems("ui"):
781 if name == 'ignore' or name.startswith('ignore.'):
805 if name == 'ignore' or name.startswith('ignore.'):
782 # we need to use os.path.join here rather than self._join
806 # we need to use os.path.join here rather than self._join
783 # because path is arbitrary and user-specified
807 # because path is arbitrary and user-specified
784 files.append(os.path.join(self._rootdir, util.expandpath(path)))
808 files.append(os.path.join(self._rootdir, util.expandpath(path)))
785 return files
809 return files
786
810
787 def _ignorefileandline(self, f):
811 def _ignorefileandline(self, f):
788 files = collections.deque(self._ignorefiles())
812 files = collections.deque(self._ignorefiles())
789 visited = set()
813 visited = set()
790 while files:
814 while files:
791 i = files.popleft()
815 i = files.popleft()
792 patterns = matchmod.readpatternfile(i, self._ui.warn,
816 patterns = matchmod.readpatternfile(i, self._ui.warn,
793 sourceinfo=True)
817 sourceinfo=True)
794 for pattern, lineno, line in patterns:
818 for pattern, lineno, line in patterns:
795 kind, p = matchmod._patsplit(pattern, 'glob')
819 kind, p = matchmod._patsplit(pattern, 'glob')
796 if kind == "subinclude":
820 if kind == "subinclude":
797 if p not in visited:
821 if p not in visited:
798 files.append(p)
822 files.append(p)
799 continue
823 continue
800 m = matchmod.match(self._root, '', [], [pattern],
824 m = matchmod.match(self._root, '', [], [pattern],
801 warn=self._ui.warn)
825 warn=self._ui.warn)
802 if m(f):
826 if m(f):
803 return (i, lineno, line)
827 return (i, lineno, line)
804 visited.add(i)
828 visited.add(i)
805 return (None, -1, "")
829 return (None, -1, "")
806
830
807 def _walkexplicit(self, match, subrepos):
831 def _walkexplicit(self, match, subrepos):
808 '''Get stat data about the files explicitly specified by match.
832 '''Get stat data about the files explicitly specified by match.
809
833
810 Return a triple (results, dirsfound, dirsnotfound).
834 Return a triple (results, dirsfound, dirsnotfound).
811 - results is a mapping from filename to stat result. It also contains
835 - results is a mapping from filename to stat result. It also contains
812 listings mapping subrepos and .hg to None.
836 listings mapping subrepos and .hg to None.
813 - dirsfound is a list of files found to be directories.
837 - dirsfound is a list of files found to be directories.
814 - dirsnotfound is a list of files that the dirstate thinks are
838 - dirsnotfound is a list of files that the dirstate thinks are
815 directories and that were not found.'''
839 directories and that were not found.'''
816
840
817 def badtype(mode):
841 def badtype(mode):
818 kind = _('unknown')
842 kind = _('unknown')
819 if stat.S_ISCHR(mode):
843 if stat.S_ISCHR(mode):
820 kind = _('character device')
844 kind = _('character device')
821 elif stat.S_ISBLK(mode):
845 elif stat.S_ISBLK(mode):
822 kind = _('block device')
846 kind = _('block device')
823 elif stat.S_ISFIFO(mode):
847 elif stat.S_ISFIFO(mode):
824 kind = _('fifo')
848 kind = _('fifo')
825 elif stat.S_ISSOCK(mode):
849 elif stat.S_ISSOCK(mode):
826 kind = _('socket')
850 kind = _('socket')
827 elif stat.S_ISDIR(mode):
851 elif stat.S_ISDIR(mode):
828 kind = _('directory')
852 kind = _('directory')
829 return _('unsupported file type (type is %s)') % kind
853 return _('unsupported file type (type is %s)') % kind
830
854
831 matchedir = match.explicitdir
855 matchedir = match.explicitdir
832 badfn = match.bad
856 badfn = match.bad
833 dmap = self._map
857 dmap = self._map
834 lstat = os.lstat
858 lstat = os.lstat
835 getkind = stat.S_IFMT
859 getkind = stat.S_IFMT
836 dirkind = stat.S_IFDIR
860 dirkind = stat.S_IFDIR
837 regkind = stat.S_IFREG
861 regkind = stat.S_IFREG
838 lnkkind = stat.S_IFLNK
862 lnkkind = stat.S_IFLNK
839 join = self._join
863 join = self._join
840 dirsfound = []
864 dirsfound = []
841 foundadd = dirsfound.append
865 foundadd = dirsfound.append
842 dirsnotfound = []
866 dirsnotfound = []
843 notfoundadd = dirsnotfound.append
867 notfoundadd = dirsnotfound.append
844
868
845 if not match.isexact() and self._checkcase:
869 if not match.isexact() and self._checkcase:
846 normalize = self._normalize
870 normalize = self._normalize
847 else:
871 else:
848 normalize = None
872 normalize = None
849
873
850 files = sorted(match.files())
874 files = sorted(match.files())
851 subrepos.sort()
875 subrepos.sort()
852 i, j = 0, 0
876 i, j = 0, 0
853 while i < len(files) and j < len(subrepos):
877 while i < len(files) and j < len(subrepos):
854 subpath = subrepos[j] + "/"
878 subpath = subrepos[j] + "/"
855 if files[i] < subpath:
879 if files[i] < subpath:
856 i += 1
880 i += 1
857 continue
881 continue
858 while i < len(files) and files[i].startswith(subpath):
882 while i < len(files) and files[i].startswith(subpath):
859 del files[i]
883 del files[i]
860 j += 1
884 j += 1
861
885
862 if not files or '.' in files:
886 if not files or '.' in files:
863 files = ['.']
887 files = ['.']
864 results = dict.fromkeys(subrepos)
888 results = dict.fromkeys(subrepos)
865 results['.hg'] = None
889 results['.hg'] = None
866
890
867 alldirs = None
891 alldirs = None
868 for ff in files:
892 for ff in files:
869 # constructing the foldmap is expensive, so don't do it for the
893 # constructing the foldmap is expensive, so don't do it for the
870 # common case where files is ['.']
894 # common case where files is ['.']
871 if normalize and ff != '.':
895 if normalize and ff != '.':
872 nf = normalize(ff, False, True)
896 nf = normalize(ff, False, True)
873 else:
897 else:
874 nf = ff
898 nf = ff
875 if nf in results:
899 if nf in results:
876 continue
900 continue
877
901
878 try:
902 try:
879 st = lstat(join(nf))
903 st = lstat(join(nf))
880 kind = getkind(st.st_mode)
904 kind = getkind(st.st_mode)
881 if kind == dirkind:
905 if kind == dirkind:
882 if nf in dmap:
906 if nf in dmap:
883 # file replaced by dir on disk but still in dirstate
907 # file replaced by dir on disk but still in dirstate
884 results[nf] = None
908 results[nf] = None
885 if matchedir:
909 if matchedir:
886 matchedir(nf)
910 matchedir(nf)
887 foundadd((nf, ff))
911 foundadd((nf, ff))
888 elif kind == regkind or kind == lnkkind:
912 elif kind == regkind or kind == lnkkind:
889 results[nf] = st
913 results[nf] = st
890 else:
914 else:
891 badfn(ff, badtype(kind))
915 badfn(ff, badtype(kind))
892 if nf in dmap:
916 if nf in dmap:
893 results[nf] = None
917 results[nf] = None
894 except OSError as inst: # nf not found on disk - it is dirstate only
918 except OSError as inst: # nf not found on disk - it is dirstate only
895 if nf in dmap: # does it exactly match a missing file?
919 if nf in dmap: # does it exactly match a missing file?
896 results[nf] = None
920 results[nf] = None
897 else: # does it match a missing directory?
921 else: # does it match a missing directory?
898 if alldirs is None:
922 if alldirs is None:
899 alldirs = util.dirs(dmap)
923 alldirs = util.dirs(dmap)
900 if nf in alldirs:
924 if nf in alldirs:
901 if matchedir:
925 if matchedir:
902 matchedir(nf)
926 matchedir(nf)
903 notfoundadd(nf)
927 notfoundadd(nf)
904 else:
928 else:
905 badfn(ff, inst.strerror)
929 badfn(ff, inst.strerror)
906
930
907 # Case insensitive filesystems cannot rely on lstat() failing to detect
931 # Case insensitive filesystems cannot rely on lstat() failing to detect
908 # a case-only rename. Prune the stat object for any file that does not
932 # a case-only rename. Prune the stat object for any file that does not
909 # match the case in the filesystem, if there are multiple files that
933 # match the case in the filesystem, if there are multiple files that
910 # normalize to the same path.
934 # normalize to the same path.
911 if match.isexact() and self._checkcase:
935 if match.isexact() and self._checkcase:
912 normed = {}
936 normed = {}
913
937
914 for f, st in results.iteritems():
938 for f, st in results.iteritems():
915 if st is None:
939 if st is None:
916 continue
940 continue
917
941
918 nc = util.normcase(f)
942 nc = util.normcase(f)
919 paths = normed.get(nc)
943 paths = normed.get(nc)
920
944
921 if paths is None:
945 if paths is None:
922 paths = set()
946 paths = set()
923 normed[nc] = paths
947 normed[nc] = paths
924
948
925 paths.add(f)
949 paths.add(f)
926
950
927 for norm, paths in normed.iteritems():
951 for norm, paths in normed.iteritems():
928 if len(paths) > 1:
952 if len(paths) > 1:
929 for path in paths:
953 for path in paths:
930 folded = self._discoverpath(path, norm, True, None,
954 folded = self._discoverpath(path, norm, True, None,
931 self._dirfoldmap)
955 self._dirfoldmap)
932 if path != folded:
956 if path != folded:
933 results[path] = None
957 results[path] = None
934
958
935 return results, dirsfound, dirsnotfound
959 return results, dirsfound, dirsnotfound
936
960
937 def walk(self, match, subrepos, unknown, ignored, full=True):
961 def walk(self, match, subrepos, unknown, ignored, full=True):
938 '''
962 '''
939 Walk recursively through the directory tree, finding all files
963 Walk recursively through the directory tree, finding all files
940 matched by match.
964 matched by match.
941
965
942 If full is False, maybe skip some known-clean files.
966 If full is False, maybe skip some known-clean files.
943
967
944 Return a dict mapping filename to stat-like object (either
968 Return a dict mapping filename to stat-like object (either
945 mercurial.osutil.stat instance or return value of os.stat()).
969 mercurial.osutil.stat instance or return value of os.stat()).
946
970
947 '''
971 '''
948 # full is a flag that extensions that hook into walk can use -- this
972 # full is a flag that extensions that hook into walk can use -- this
949 # implementation doesn't use it at all. This satisfies the contract
973 # implementation doesn't use it at all. This satisfies the contract
950 # because we only guarantee a "maybe".
974 # because we only guarantee a "maybe".
951
975
952 if ignored:
976 if ignored:
953 ignore = util.never
977 ignore = util.never
954 dirignore = util.never
978 dirignore = util.never
955 elif unknown:
979 elif unknown:
956 ignore = self._ignore
980 ignore = self._ignore
957 dirignore = self._dirignore
981 dirignore = self._dirignore
958 else:
982 else:
959 # if not unknown and not ignored, drop dir recursion and step 2
983 # if not unknown and not ignored, drop dir recursion and step 2
960 ignore = util.always
984 ignore = util.always
961 dirignore = util.always
985 dirignore = util.always
962
986
963 matchfn = match.matchfn
987 matchfn = match.matchfn
964 matchalways = match.always()
988 matchalways = match.always()
965 matchtdir = match.traversedir
989 matchtdir = match.traversedir
966 dmap = self._map
990 dmap = self._map
967 listdir = osutil.listdir
991 listdir = osutil.listdir
968 lstat = os.lstat
992 lstat = os.lstat
969 dirkind = stat.S_IFDIR
993 dirkind = stat.S_IFDIR
970 regkind = stat.S_IFREG
994 regkind = stat.S_IFREG
971 lnkkind = stat.S_IFLNK
995 lnkkind = stat.S_IFLNK
972 join = self._join
996 join = self._join
973
997
974 exact = skipstep3 = False
998 exact = skipstep3 = False
975 if match.isexact(): # match.exact
999 if match.isexact(): # match.exact
976 exact = True
1000 exact = True
977 dirignore = util.always # skip step 2
1001 dirignore = util.always # skip step 2
978 elif match.prefix(): # match.match, no patterns
1002 elif match.prefix(): # match.match, no patterns
979 skipstep3 = True
1003 skipstep3 = True
980
1004
981 if not exact and self._checkcase:
1005 if not exact and self._checkcase:
982 normalize = self._normalize
1006 normalize = self._normalize
983 normalizefile = self._normalizefile
1007 normalizefile = self._normalizefile
984 skipstep3 = False
1008 skipstep3 = False
985 else:
1009 else:
986 normalize = self._normalize
1010 normalize = self._normalize
987 normalizefile = None
1011 normalizefile = None
988
1012
989 # step 1: find all explicit files
1013 # step 1: find all explicit files
990 results, work, dirsnotfound = self._walkexplicit(match, subrepos)
1014 results, work, dirsnotfound = self._walkexplicit(match, subrepos)
991
1015
992 skipstep3 = skipstep3 and not (work or dirsnotfound)
1016 skipstep3 = skipstep3 and not (work or dirsnotfound)
993 work = [d for d in work if not dirignore(d[0])]
1017 work = [d for d in work if not dirignore(d[0])]
994
1018
995 # step 2: visit subdirectories
1019 # step 2: visit subdirectories
996 def traverse(work, alreadynormed):
1020 def traverse(work, alreadynormed):
997 wadd = work.append
1021 wadd = work.append
998 while work:
1022 while work:
999 nd = work.pop()
1023 nd = work.pop()
1000 skip = None
1024 skip = None
1001 if nd == '.':
1025 if nd == '.':
1002 nd = ''
1026 nd = ''
1003 else:
1027 else:
1004 skip = '.hg'
1028 skip = '.hg'
1005 try:
1029 try:
1006 entries = listdir(join(nd), stat=True, skip=skip)
1030 entries = listdir(join(nd), stat=True, skip=skip)
1007 except OSError as inst:
1031 except OSError as inst:
1008 if inst.errno in (errno.EACCES, errno.ENOENT):
1032 if inst.errno in (errno.EACCES, errno.ENOENT):
1009 match.bad(self.pathto(nd), inst.strerror)
1033 match.bad(self.pathto(nd), inst.strerror)
1010 continue
1034 continue
1011 raise
1035 raise
1012 for f, kind, st in entries:
1036 for f, kind, st in entries:
1013 if normalizefile:
1037 if normalizefile:
1014 # even though f might be a directory, we're only
1038 # even though f might be a directory, we're only
1015 # interested in comparing it to files currently in the
1039 # interested in comparing it to files currently in the
1016 # dmap -- therefore normalizefile is enough
1040 # dmap -- therefore normalizefile is enough
1017 nf = normalizefile(nd and (nd + "/" + f) or f, True,
1041 nf = normalizefile(nd and (nd + "/" + f) or f, True,
1018 True)
1042 True)
1019 else:
1043 else:
1020 nf = nd and (nd + "/" + f) or f
1044 nf = nd and (nd + "/" + f) or f
1021 if nf not in results:
1045 if nf not in results:
1022 if kind == dirkind:
1046 if kind == dirkind:
1023 if not ignore(nf):
1047 if not ignore(nf):
1024 if matchtdir:
1048 if matchtdir:
1025 matchtdir(nf)
1049 matchtdir(nf)
1026 wadd(nf)
1050 wadd(nf)
1027 if nf in dmap and (matchalways or matchfn(nf)):
1051 if nf in dmap and (matchalways or matchfn(nf)):
1028 results[nf] = None
1052 results[nf] = None
1029 elif kind == regkind or kind == lnkkind:
1053 elif kind == regkind or kind == lnkkind:
1030 if nf in dmap:
1054 if nf in dmap:
1031 if matchalways or matchfn(nf):
1055 if matchalways or matchfn(nf):
1032 results[nf] = st
1056 results[nf] = st
1033 elif ((matchalways or matchfn(nf))
1057 elif ((matchalways or matchfn(nf))
1034 and not ignore(nf)):
1058 and not ignore(nf)):
1035 # unknown file -- normalize if necessary
1059 # unknown file -- normalize if necessary
1036 if not alreadynormed:
1060 if not alreadynormed:
1037 nf = normalize(nf, False, True)
1061 nf = normalize(nf, False, True)
1038 results[nf] = st
1062 results[nf] = st
1039 elif nf in dmap and (matchalways or matchfn(nf)):
1063 elif nf in dmap and (matchalways or matchfn(nf)):
1040 results[nf] = None
1064 results[nf] = None
1041
1065
1042 for nd, d in work:
1066 for nd, d in work:
1043 # alreadynormed means that processwork doesn't have to do any
1067 # alreadynormed means that processwork doesn't have to do any
1044 # expensive directory normalization
1068 # expensive directory normalization
1045 alreadynormed = not normalize or nd == d
1069 alreadynormed = not normalize or nd == d
1046 traverse([d], alreadynormed)
1070 traverse([d], alreadynormed)
1047
1071
1048 for s in subrepos:
1072 for s in subrepos:
1049 del results[s]
1073 del results[s]
1050 del results['.hg']
1074 del results['.hg']
1051
1075
1052 # step 3: visit remaining files from dmap
1076 # step 3: visit remaining files from dmap
1053 if not skipstep3 and not exact:
1077 if not skipstep3 and not exact:
1054 # If a dmap file is not in results yet, it was either
1078 # If a dmap file is not in results yet, it was either
1055 # a) not matching matchfn b) ignored, c) missing, or d) under a
1079 # a) not matching matchfn b) ignored, c) missing, or d) under a
1056 # symlink directory.
1080 # symlink directory.
1057 if not results and matchalways:
1081 if not results and matchalways:
1058 visit = dmap.keys()
1082 visit = dmap.keys()
1059 else:
1083 else:
1060 visit = [f for f in dmap if f not in results and matchfn(f)]
1084 visit = [f for f in dmap if f not in results and matchfn(f)]
1061 visit.sort()
1085 visit.sort()
1062
1086
1063 if unknown:
1087 if unknown:
1064 # unknown == True means we walked all dirs under the roots
1088 # unknown == True means we walked all dirs under the roots
1065 # that wasn't ignored, and everything that matched was stat'ed
1089 # that wasn't ignored, and everything that matched was stat'ed
1066 # and is already in results.
1090 # and is already in results.
1067 # The rest must thus be ignored or under a symlink.
1091 # The rest must thus be ignored or under a symlink.
1068 audit_path = pathutil.pathauditor(self._root)
1092 audit_path = pathutil.pathauditor(self._root)
1069
1093
1070 for nf in iter(visit):
1094 for nf in iter(visit):
1071 # If a stat for the same file was already added with a
1095 # If a stat for the same file was already added with a
1072 # different case, don't add one for this, since that would
1096 # different case, don't add one for this, since that would
1073 # make it appear as if the file exists under both names
1097 # make it appear as if the file exists under both names
1074 # on disk.
1098 # on disk.
1075 if (normalizefile and
1099 if (normalizefile and
1076 normalizefile(nf, True, True) in results):
1100 normalizefile(nf, True, True) in results):
1077 results[nf] = None
1101 results[nf] = None
1078 # Report ignored items in the dmap as long as they are not
1102 # Report ignored items in the dmap as long as they are not
1079 # under a symlink directory.
1103 # under a symlink directory.
1080 elif audit_path.check(nf):
1104 elif audit_path.check(nf):
1081 try:
1105 try:
1082 results[nf] = lstat(join(nf))
1106 results[nf] = lstat(join(nf))
1083 # file was just ignored, no links, and exists
1107 # file was just ignored, no links, and exists
1084 except OSError:
1108 except OSError:
1085 # file doesn't exist
1109 # file doesn't exist
1086 results[nf] = None
1110 results[nf] = None
1087 else:
1111 else:
1088 # It's either missing or under a symlink directory
1112 # It's either missing or under a symlink directory
1089 # which we in this case report as missing
1113 # which we in this case report as missing
1090 results[nf] = None
1114 results[nf] = None
1091 else:
1115 else:
1092 # We may not have walked the full directory tree above,
1116 # We may not have walked the full directory tree above,
1093 # so stat and check everything we missed.
1117 # so stat and check everything we missed.
1094 nf = iter(visit).next
1118 nf = iter(visit).next
1095 for st in util.statfiles([join(i) for i in visit]):
1119 for st in util.statfiles([join(i) for i in visit]):
1096 results[nf()] = st
1120 results[nf()] = st
1097 return results
1121 return results
1098
1122
1099 def status(self, match, subrepos, ignored, clean, unknown):
1123 def status(self, match, subrepos, ignored, clean, unknown):
1100 '''Determine the status of the working copy relative to the
1124 '''Determine the status of the working copy relative to the
1101 dirstate and return a pair of (unsure, status), where status is of type
1125 dirstate and return a pair of (unsure, status), where status is of type
1102 scmutil.status and:
1126 scmutil.status and:
1103
1127
1104 unsure:
1128 unsure:
1105 files that might have been modified since the dirstate was
1129 files that might have been modified since the dirstate was
1106 written, but need to be read to be sure (size is the same
1130 written, but need to be read to be sure (size is the same
1107 but mtime differs)
1131 but mtime differs)
1108 status.modified:
1132 status.modified:
1109 files that have definitely been modified since the dirstate
1133 files that have definitely been modified since the dirstate
1110 was written (different size or mode)
1134 was written (different size or mode)
1111 status.clean:
1135 status.clean:
1112 files that have definitely not been modified since the
1136 files that have definitely not been modified since the
1113 dirstate was written
1137 dirstate was written
1114 '''
1138 '''
1115 listignored, listclean, listunknown = ignored, clean, unknown
1139 listignored, listclean, listunknown = ignored, clean, unknown
1116 lookup, modified, added, unknown, ignored = [], [], [], [], []
1140 lookup, modified, added, unknown, ignored = [], [], [], [], []
1117 removed, deleted, clean = [], [], []
1141 removed, deleted, clean = [], [], []
1118
1142
1119 dmap = self._map
1143 dmap = self._map
1120 ladd = lookup.append # aka "unsure"
1144 ladd = lookup.append # aka "unsure"
1121 madd = modified.append
1145 madd = modified.append
1122 aadd = added.append
1146 aadd = added.append
1123 uadd = unknown.append
1147 uadd = unknown.append
1124 iadd = ignored.append
1148 iadd = ignored.append
1125 radd = removed.append
1149 radd = removed.append
1126 dadd = deleted.append
1150 dadd = deleted.append
1127 cadd = clean.append
1151 cadd = clean.append
1128 mexact = match.exact
1152 mexact = match.exact
1129 dirignore = self._dirignore
1153 dirignore = self._dirignore
1130 checkexec = self._checkexec
1154 checkexec = self._checkexec
1131 copymap = self._copymap
1155 copymap = self._copymap
1132 lastnormaltime = self._lastnormaltime
1156 lastnormaltime = self._lastnormaltime
1133
1157
1134 # We need to do full walks when either
1158 # We need to do full walks when either
1135 # - we're listing all clean files, or
1159 # - we're listing all clean files, or
1136 # - match.traversedir does something, because match.traversedir should
1160 # - match.traversedir does something, because match.traversedir should
1137 # be called for every dir in the working dir
1161 # be called for every dir in the working dir
1138 full = listclean or match.traversedir is not None
1162 full = listclean or match.traversedir is not None
1139 for fn, st in self.walk(match, subrepos, listunknown, listignored,
1163 for fn, st in self.walk(match, subrepos, listunknown, listignored,
1140 full=full).iteritems():
1164 full=full).iteritems():
1141 if fn not in dmap:
1165 if fn not in dmap:
1142 if (listignored or mexact(fn)) and dirignore(fn):
1166 if (listignored or mexact(fn)) and dirignore(fn):
1143 if listignored:
1167 if listignored:
1144 iadd(fn)
1168 iadd(fn)
1145 else:
1169 else:
1146 uadd(fn)
1170 uadd(fn)
1147 continue
1171 continue
1148
1172
1149 # This is equivalent to 'state, mode, size, time = dmap[fn]' but not
1173 # This is equivalent to 'state, mode, size, time = dmap[fn]' but not
1150 # written like that for performance reasons. dmap[fn] is not a
1174 # written like that for performance reasons. dmap[fn] is not a
1151 # Python tuple in compiled builds. The CPython UNPACK_SEQUENCE
1175 # Python tuple in compiled builds. The CPython UNPACK_SEQUENCE
1152 # opcode has fast paths when the value to be unpacked is a tuple or
1176 # opcode has fast paths when the value to be unpacked is a tuple or
1153 # a list, but falls back to creating a full-fledged iterator in
1177 # a list, but falls back to creating a full-fledged iterator in
1154 # general. That is much slower than simply accessing and storing the
1178 # general. That is much slower than simply accessing and storing the
1155 # tuple members one by one.
1179 # tuple members one by one.
1156 t = dmap[fn]
1180 t = dmap[fn]
1157 state = t[0]
1181 state = t[0]
1158 mode = t[1]
1182 mode = t[1]
1159 size = t[2]
1183 size = t[2]
1160 time = t[3]
1184 time = t[3]
1161
1185
1162 if not st and state in "nma":
1186 if not st and state in "nma":
1163 dadd(fn)
1187 dadd(fn)
1164 elif state == 'n':
1188 elif state == 'n':
1165 if (size >= 0 and
1189 if (size >= 0 and
1166 ((size != st.st_size and size != st.st_size & _rangemask)
1190 ((size != st.st_size and size != st.st_size & _rangemask)
1167 or ((mode ^ st.st_mode) & 0o100 and checkexec))
1191 or ((mode ^ st.st_mode) & 0o100 and checkexec))
1168 or size == -2 # other parent
1192 or size == -2 # other parent
1169 or fn in copymap):
1193 or fn in copymap):
1170 madd(fn)
1194 madd(fn)
1171 elif time != st.st_mtime and time != st.st_mtime & _rangemask:
1195 elif time != st.st_mtime and time != st.st_mtime & _rangemask:
1172 ladd(fn)
1196 ladd(fn)
1173 elif st.st_mtime == lastnormaltime:
1197 elif st.st_mtime == lastnormaltime:
1174 # fn may have just been marked as normal and it may have
1198 # fn may have just been marked as normal and it may have
1175 # changed in the same second without changing its size.
1199 # changed in the same second without changing its size.
1176 # This can happen if we quickly do multiple commits.
1200 # This can happen if we quickly do multiple commits.
1177 # Force lookup, so we don't miss such a racy file change.
1201 # Force lookup, so we don't miss such a racy file change.
1178 ladd(fn)
1202 ladd(fn)
1179 elif listclean:
1203 elif listclean:
1180 cadd(fn)
1204 cadd(fn)
1181 elif state == 'm':
1205 elif state == 'm':
1182 madd(fn)
1206 madd(fn)
1183 elif state == 'a':
1207 elif state == 'a':
1184 aadd(fn)
1208 aadd(fn)
1185 elif state == 'r':
1209 elif state == 'r':
1186 radd(fn)
1210 radd(fn)
1187
1211
1188 return (lookup, scmutil.status(modified, added, removed, deleted,
1212 return (lookup, scmutil.status(modified, added, removed, deleted,
1189 unknown, ignored, clean))
1213 unknown, ignored, clean))
1190
1214
1191 def matches(self, match):
1215 def matches(self, match):
1192 '''
1216 '''
1193 return files in the dirstate (in whatever state) filtered by match
1217 return files in the dirstate (in whatever state) filtered by match
1194 '''
1218 '''
1195 dmap = self._map
1219 dmap = self._map
1196 if match.always():
1220 if match.always():
1197 return dmap.keys()
1221 return dmap.keys()
1198 files = match.files()
1222 files = match.files()
1199 if match.isexact():
1223 if match.isexact():
1200 # fast path -- filter the other way around, since typically files is
1224 # fast path -- filter the other way around, since typically files is
1201 # much smaller than dmap
1225 # much smaller than dmap
1202 return [f for f in files if f in dmap]
1226 return [f for f in files if f in dmap]
1203 if match.prefix() and all(fn in dmap for fn in files):
1227 if match.prefix() and all(fn in dmap for fn in files):
1204 # fast path -- all the values are known to be files, so just return
1228 # fast path -- all the values are known to be files, so just return
1205 # that
1229 # that
1206 return list(files)
1230 return list(files)
1207 return [f for f in dmap if match(f)]
1231 return [f for f in dmap if match(f)]
1208
1232
1209 def _actualfilename(self, tr):
1233 def _actualfilename(self, tr):
1210 if tr:
1234 if tr:
1211 return self._pendingfilename
1235 return self._pendingfilename
1212 else:
1236 else:
1213 return self._filename
1237 return self._filename
1214
1238
1215 def savebackup(self, tr, suffix='', prefix=''):
1239 def savebackup(self, tr, suffix='', prefix=''):
1216 '''Save current dirstate into backup file with suffix'''
1240 '''Save current dirstate into backup file with suffix'''
1217 assert len(suffix) > 0 or len(prefix) > 0
1241 assert len(suffix) > 0 or len(prefix) > 0
1218 filename = self._actualfilename(tr)
1242 filename = self._actualfilename(tr)
1219
1243
1220 # use '_writedirstate' instead of 'write' to write changes certainly,
1244 # use '_writedirstate' instead of 'write' to write changes certainly,
1221 # because the latter omits writing out if transaction is running.
1245 # because the latter omits writing out if transaction is running.
1222 # output file will be used to create backup of dirstate at this point.
1246 # output file will be used to create backup of dirstate at this point.
1223 if self._dirty or not self._opener.exists(filename):
1247 if self._dirty or not self._opener.exists(filename):
1224 self._writedirstate(self._opener(filename, "w", atomictemp=True,
1248 self._writedirstate(self._opener(filename, "w", atomictemp=True,
1225 checkambig=True))
1249 checkambig=True))
1226
1250
1227 if tr:
1251 if tr:
1228 # ensure that subsequent tr.writepending returns True for
1252 # ensure that subsequent tr.writepending returns True for
1229 # changes written out above, even if dirstate is never
1253 # changes written out above, even if dirstate is never
1230 # changed after this
1254 # changed after this
1231 tr.addfilegenerator('dirstate', (self._filename,),
1255 tr.addfilegenerator('dirstate', (self._filename,),
1232 self._writedirstate, location='plain')
1256 self._writedirstate, location='plain')
1233
1257
1234 # ensure that pending file written above is unlinked at
1258 # ensure that pending file written above is unlinked at
1235 # failure, even if tr.writepending isn't invoked until the
1259 # failure, even if tr.writepending isn't invoked until the
1236 # end of this transaction
1260 # end of this transaction
1237 tr.registertmp(filename, location='plain')
1261 tr.registertmp(filename, location='plain')
1238
1262
1239 backupname = prefix + self._filename + suffix
1263 backupname = prefix + self._filename + suffix
1240 assert backupname != filename
1264 assert backupname != filename
1241 if self._opener.exists(backupname):
1265 if self._opener.exists(backupname):
1242 self._opener.unlink(backupname)
1266 self._opener.unlink(backupname)
1243 # hardlink backup is okay because _writedirstate is always called
1267 # hardlink backup is okay because _writedirstate is always called
1244 # with an "atomictemp=True" file.
1268 # with an "atomictemp=True" file.
1245 util.copyfile(self._opener.join(filename),
1269 util.copyfile(self._opener.join(filename),
1246 self._opener.join(backupname), hardlink=True)
1270 self._opener.join(backupname), hardlink=True)
1247
1271
1248 def restorebackup(self, tr, suffix='', prefix=''):
1272 def restorebackup(self, tr, suffix='', prefix=''):
1249 '''Restore dirstate by backup file with suffix'''
1273 '''Restore dirstate by backup file with suffix'''
1250 assert len(suffix) > 0 or len(prefix) > 0
1274 assert len(suffix) > 0 or len(prefix) > 0
1251 # this "invalidate()" prevents "wlock.release()" from writing
1275 # this "invalidate()" prevents "wlock.release()" from writing
1252 # changes of dirstate out after restoring from backup file
1276 # changes of dirstate out after restoring from backup file
1253 self.invalidate()
1277 self.invalidate()
1254 filename = self._actualfilename(tr)
1278 filename = self._actualfilename(tr)
1255 # using self._filename to avoid having "pending" in the backup filename
1279 # using self._filename to avoid having "pending" in the backup filename
1256 self._opener.rename(prefix + self._filename + suffix, filename,
1280 self._opener.rename(prefix + self._filename + suffix, filename,
1257 checkambig=True)
1281 checkambig=True)
1258
1282
1259 def clearbackup(self, tr, suffix='', prefix=''):
1283 def clearbackup(self, tr, suffix='', prefix=''):
1260 '''Clear backup file with suffix'''
1284 '''Clear backup file with suffix'''
1261 assert len(suffix) > 0 or len(prefix) > 0
1285 assert len(suffix) > 0 or len(prefix) > 0
1262 # using self._filename to avoid having "pending" in the backup filename
1286 # using self._filename to avoid having "pending" in the backup filename
1263 self._opener.unlink(prefix + self._filename + suffix)
1287 self._opener.unlink(prefix + self._filename + suffix)
@@ -1,2925 +1,2966 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 #include "bitmanipulation.h"
16 #include "bitmanipulation.h"
17
17
18 #ifdef IS_PY3K
18 #ifdef IS_PY3K
19 /* The mapping of Python types is meant to be temporary to get Python
19 /* The mapping of Python types is meant to be temporary to get Python
20 * 3 to compile. We should remove this once Python 3 support is fully
20 * 3 to compile. We should remove this once Python 3 support is fully
21 * supported and proper types are used in the extensions themselves. */
21 * supported and proper types are used in the extensions themselves. */
22 #define PyInt_Type PyLong_Type
22 #define PyInt_Type PyLong_Type
23 #define PyInt_Check PyLong_Check
23 #define PyInt_Check PyLong_Check
24 #define PyInt_FromLong PyLong_FromLong
24 #define PyInt_FromLong PyLong_FromLong
25 #define PyInt_FromSsize_t PyLong_FromSsize_t
25 #define PyInt_FromSsize_t PyLong_FromSsize_t
26 #define PyInt_AS_LONG PyLong_AS_LONG
26 #define PyInt_AS_LONG PyLong_AS_LONG
27 #define PyInt_AsLong PyLong_AsLong
27 #define PyInt_AsLong PyLong_AsLong
28 #endif
28 #endif
29
29
30 static char *versionerrortext = "Python minor version mismatch";
30 static char *versionerrortext = "Python minor version mismatch";
31
31
32 static int8_t hextable[256] = {
32 static int8_t hextable[256] = {
33 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
33 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
34 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
34 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
35 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
35 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
36 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, /* 0-9 */
36 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, /* 0-9 */
37 -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* A-F */
37 -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* A-F */
38 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
38 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
39 -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* a-f */
39 -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* a-f */
40 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
40 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
41 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
41 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
42 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
42 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
43 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
43 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
44 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
44 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
45 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
45 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
46 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
46 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
47 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
47 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
48 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
48 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
49 };
49 };
50
50
51 static char lowertable[128] = {
51 static char lowertable[128] = {
52 '\x00', '\x01', '\x02', '\x03', '\x04', '\x05', '\x06', '\x07',
52 '\x00', '\x01', '\x02', '\x03', '\x04', '\x05', '\x06', '\x07',
53 '\x08', '\x09', '\x0a', '\x0b', '\x0c', '\x0d', '\x0e', '\x0f',
53 '\x08', '\x09', '\x0a', '\x0b', '\x0c', '\x0d', '\x0e', '\x0f',
54 '\x10', '\x11', '\x12', '\x13', '\x14', '\x15', '\x16', '\x17',
54 '\x10', '\x11', '\x12', '\x13', '\x14', '\x15', '\x16', '\x17',
55 '\x18', '\x19', '\x1a', '\x1b', '\x1c', '\x1d', '\x1e', '\x1f',
55 '\x18', '\x19', '\x1a', '\x1b', '\x1c', '\x1d', '\x1e', '\x1f',
56 '\x20', '\x21', '\x22', '\x23', '\x24', '\x25', '\x26', '\x27',
56 '\x20', '\x21', '\x22', '\x23', '\x24', '\x25', '\x26', '\x27',
57 '\x28', '\x29', '\x2a', '\x2b', '\x2c', '\x2d', '\x2e', '\x2f',
57 '\x28', '\x29', '\x2a', '\x2b', '\x2c', '\x2d', '\x2e', '\x2f',
58 '\x30', '\x31', '\x32', '\x33', '\x34', '\x35', '\x36', '\x37',
58 '\x30', '\x31', '\x32', '\x33', '\x34', '\x35', '\x36', '\x37',
59 '\x38', '\x39', '\x3a', '\x3b', '\x3c', '\x3d', '\x3e', '\x3f',
59 '\x38', '\x39', '\x3a', '\x3b', '\x3c', '\x3d', '\x3e', '\x3f',
60 '\x40',
60 '\x40',
61 '\x61', '\x62', '\x63', '\x64', '\x65', '\x66', '\x67', /* A-G */
61 '\x61', '\x62', '\x63', '\x64', '\x65', '\x66', '\x67', /* A-G */
62 '\x68', '\x69', '\x6a', '\x6b', '\x6c', '\x6d', '\x6e', '\x6f', /* H-O */
62 '\x68', '\x69', '\x6a', '\x6b', '\x6c', '\x6d', '\x6e', '\x6f', /* H-O */
63 '\x70', '\x71', '\x72', '\x73', '\x74', '\x75', '\x76', '\x77', /* P-W */
63 '\x70', '\x71', '\x72', '\x73', '\x74', '\x75', '\x76', '\x77', /* P-W */
64 '\x78', '\x79', '\x7a', /* X-Z */
64 '\x78', '\x79', '\x7a', /* X-Z */
65 '\x5b', '\x5c', '\x5d', '\x5e', '\x5f',
65 '\x5b', '\x5c', '\x5d', '\x5e', '\x5f',
66 '\x60', '\x61', '\x62', '\x63', '\x64', '\x65', '\x66', '\x67',
66 '\x60', '\x61', '\x62', '\x63', '\x64', '\x65', '\x66', '\x67',
67 '\x68', '\x69', '\x6a', '\x6b', '\x6c', '\x6d', '\x6e', '\x6f',
67 '\x68', '\x69', '\x6a', '\x6b', '\x6c', '\x6d', '\x6e', '\x6f',
68 '\x70', '\x71', '\x72', '\x73', '\x74', '\x75', '\x76', '\x77',
68 '\x70', '\x71', '\x72', '\x73', '\x74', '\x75', '\x76', '\x77',
69 '\x78', '\x79', '\x7a', '\x7b', '\x7c', '\x7d', '\x7e', '\x7f'
69 '\x78', '\x79', '\x7a', '\x7b', '\x7c', '\x7d', '\x7e', '\x7f'
70 };
70 };
71
71
72 static char uppertable[128] = {
72 static char uppertable[128] = {
73 '\x00', '\x01', '\x02', '\x03', '\x04', '\x05', '\x06', '\x07',
73 '\x00', '\x01', '\x02', '\x03', '\x04', '\x05', '\x06', '\x07',
74 '\x08', '\x09', '\x0a', '\x0b', '\x0c', '\x0d', '\x0e', '\x0f',
74 '\x08', '\x09', '\x0a', '\x0b', '\x0c', '\x0d', '\x0e', '\x0f',
75 '\x10', '\x11', '\x12', '\x13', '\x14', '\x15', '\x16', '\x17',
75 '\x10', '\x11', '\x12', '\x13', '\x14', '\x15', '\x16', '\x17',
76 '\x18', '\x19', '\x1a', '\x1b', '\x1c', '\x1d', '\x1e', '\x1f',
76 '\x18', '\x19', '\x1a', '\x1b', '\x1c', '\x1d', '\x1e', '\x1f',
77 '\x20', '\x21', '\x22', '\x23', '\x24', '\x25', '\x26', '\x27',
77 '\x20', '\x21', '\x22', '\x23', '\x24', '\x25', '\x26', '\x27',
78 '\x28', '\x29', '\x2a', '\x2b', '\x2c', '\x2d', '\x2e', '\x2f',
78 '\x28', '\x29', '\x2a', '\x2b', '\x2c', '\x2d', '\x2e', '\x2f',
79 '\x30', '\x31', '\x32', '\x33', '\x34', '\x35', '\x36', '\x37',
79 '\x30', '\x31', '\x32', '\x33', '\x34', '\x35', '\x36', '\x37',
80 '\x38', '\x39', '\x3a', '\x3b', '\x3c', '\x3d', '\x3e', '\x3f',
80 '\x38', '\x39', '\x3a', '\x3b', '\x3c', '\x3d', '\x3e', '\x3f',
81 '\x40', '\x41', '\x42', '\x43', '\x44', '\x45', '\x46', '\x47',
81 '\x40', '\x41', '\x42', '\x43', '\x44', '\x45', '\x46', '\x47',
82 '\x48', '\x49', '\x4a', '\x4b', '\x4c', '\x4d', '\x4e', '\x4f',
82 '\x48', '\x49', '\x4a', '\x4b', '\x4c', '\x4d', '\x4e', '\x4f',
83 '\x50', '\x51', '\x52', '\x53', '\x54', '\x55', '\x56', '\x57',
83 '\x50', '\x51', '\x52', '\x53', '\x54', '\x55', '\x56', '\x57',
84 '\x58', '\x59', '\x5a', '\x5b', '\x5c', '\x5d', '\x5e', '\x5f',
84 '\x58', '\x59', '\x5a', '\x5b', '\x5c', '\x5d', '\x5e', '\x5f',
85 '\x60',
85 '\x60',
86 '\x41', '\x42', '\x43', '\x44', '\x45', '\x46', '\x47', /* a-g */
86 '\x41', '\x42', '\x43', '\x44', '\x45', '\x46', '\x47', /* a-g */
87 '\x48', '\x49', '\x4a', '\x4b', '\x4c', '\x4d', '\x4e', '\x4f', /* h-o */
87 '\x48', '\x49', '\x4a', '\x4b', '\x4c', '\x4d', '\x4e', '\x4f', /* h-o */
88 '\x50', '\x51', '\x52', '\x53', '\x54', '\x55', '\x56', '\x57', /* p-w */
88 '\x50', '\x51', '\x52', '\x53', '\x54', '\x55', '\x56', '\x57', /* p-w */
89 '\x58', '\x59', '\x5a', /* x-z */
89 '\x58', '\x59', '\x5a', /* x-z */
90 '\x7b', '\x7c', '\x7d', '\x7e', '\x7f'
90 '\x7b', '\x7c', '\x7d', '\x7e', '\x7f'
91 };
91 };
92
92
93 static inline int hexdigit(const char *p, Py_ssize_t off)
93 static inline int hexdigit(const char *p, Py_ssize_t off)
94 {
94 {
95 int8_t val = hextable[(unsigned char)p[off]];
95 int8_t val = hextable[(unsigned char)p[off]];
96
96
97 if (val >= 0) {
97 if (val >= 0) {
98 return val;
98 return val;
99 }
99 }
100
100
101 PyErr_SetString(PyExc_ValueError, "input contains non-hex character");
101 PyErr_SetString(PyExc_ValueError, "input contains non-hex character");
102 return 0;
102 return 0;
103 }
103 }
104
104
105 /*
105 /*
106 * Turn a hex-encoded string into binary.
106 * Turn a hex-encoded string into binary.
107 */
107 */
108 PyObject *unhexlify(const char *str, int len)
108 PyObject *unhexlify(const char *str, int len)
109 {
109 {
110 PyObject *ret;
110 PyObject *ret;
111 char *d;
111 char *d;
112 int i;
112 int i;
113
113
114 ret = PyBytes_FromStringAndSize(NULL, len / 2);
114 ret = PyBytes_FromStringAndSize(NULL, len / 2);
115
115
116 if (!ret)
116 if (!ret)
117 return NULL;
117 return NULL;
118
118
119 d = PyBytes_AsString(ret);
119 d = PyBytes_AsString(ret);
120
120
121 for (i = 0; i < len;) {
121 for (i = 0; i < len;) {
122 int hi = hexdigit(str, i++);
122 int hi = hexdigit(str, i++);
123 int lo = hexdigit(str, i++);
123 int lo = hexdigit(str, i++);
124 *d++ = (hi << 4) | lo;
124 *d++ = (hi << 4) | lo;
125 }
125 }
126
126
127 return ret;
127 return ret;
128 }
128 }
129
129
130 static inline PyObject *_asciitransform(PyObject *str_obj,
130 static inline PyObject *_asciitransform(PyObject *str_obj,
131 const char table[128],
131 const char table[128],
132 PyObject *fallback_fn)
132 PyObject *fallback_fn)
133 {
133 {
134 char *str, *newstr;
134 char *str, *newstr;
135 Py_ssize_t i, len;
135 Py_ssize_t i, len;
136 PyObject *newobj = NULL;
136 PyObject *newobj = NULL;
137 PyObject *ret = NULL;
137 PyObject *ret = NULL;
138
138
139 str = PyBytes_AS_STRING(str_obj);
139 str = PyBytes_AS_STRING(str_obj);
140 len = PyBytes_GET_SIZE(str_obj);
140 len = PyBytes_GET_SIZE(str_obj);
141
141
142 newobj = PyBytes_FromStringAndSize(NULL, len);
142 newobj = PyBytes_FromStringAndSize(NULL, len);
143 if (!newobj)
143 if (!newobj)
144 goto quit;
144 goto quit;
145
145
146 newstr = PyBytes_AS_STRING(newobj);
146 newstr = PyBytes_AS_STRING(newobj);
147
147
148 for (i = 0; i < len; i++) {
148 for (i = 0; i < len; i++) {
149 char c = str[i];
149 char c = str[i];
150 if (c & 0x80) {
150 if (c & 0x80) {
151 if (fallback_fn != NULL) {
151 if (fallback_fn != NULL) {
152 ret = PyObject_CallFunctionObjArgs(fallback_fn,
152 ret = PyObject_CallFunctionObjArgs(fallback_fn,
153 str_obj, NULL);
153 str_obj, NULL);
154 } else {
154 } else {
155 PyObject *err = PyUnicodeDecodeError_Create(
155 PyObject *err = PyUnicodeDecodeError_Create(
156 "ascii", str, len, i, (i + 1),
156 "ascii", str, len, i, (i + 1),
157 "unexpected code byte");
157 "unexpected code byte");
158 PyErr_SetObject(PyExc_UnicodeDecodeError, err);
158 PyErr_SetObject(PyExc_UnicodeDecodeError, err);
159 Py_XDECREF(err);
159 Py_XDECREF(err);
160 }
160 }
161 goto quit;
161 goto quit;
162 }
162 }
163 newstr[i] = table[(unsigned char)c];
163 newstr[i] = table[(unsigned char)c];
164 }
164 }
165
165
166 ret = newobj;
166 ret = newobj;
167 Py_INCREF(ret);
167 Py_INCREF(ret);
168 quit:
168 quit:
169 Py_XDECREF(newobj);
169 Py_XDECREF(newobj);
170 return ret;
170 return ret;
171 }
171 }
172
172
173 static PyObject *asciilower(PyObject *self, PyObject *args)
173 static PyObject *asciilower(PyObject *self, PyObject *args)
174 {
174 {
175 PyObject *str_obj;
175 PyObject *str_obj;
176 if (!PyArg_ParseTuple(args, "O!:asciilower", &PyBytes_Type, &str_obj))
176 if (!PyArg_ParseTuple(args, "O!:asciilower", &PyBytes_Type, &str_obj))
177 return NULL;
177 return NULL;
178 return _asciitransform(str_obj, lowertable, NULL);
178 return _asciitransform(str_obj, lowertable, NULL);
179 }
179 }
180
180
181 static PyObject *asciiupper(PyObject *self, PyObject *args)
181 static PyObject *asciiupper(PyObject *self, PyObject *args)
182 {
182 {
183 PyObject *str_obj;
183 PyObject *str_obj;
184 if (!PyArg_ParseTuple(args, "O!:asciiupper", &PyBytes_Type, &str_obj))
184 if (!PyArg_ParseTuple(args, "O!:asciiupper", &PyBytes_Type, &str_obj))
185 return NULL;
185 return NULL;
186 return _asciitransform(str_obj, uppertable, NULL);
186 return _asciitransform(str_obj, uppertable, NULL);
187 }
187 }
188
188
189 static inline PyObject *_dict_new_presized(Py_ssize_t expected_size)
189 static inline PyObject *_dict_new_presized(Py_ssize_t expected_size)
190 {
190 {
191 /* _PyDict_NewPresized expects a minused parameter, but it actually
191 /* _PyDict_NewPresized expects a minused parameter, but it actually
192 creates a dictionary that's the nearest power of two bigger than the
192 creates a dictionary that's the nearest power of two bigger than the
193 parameter. For example, with the initial minused = 1000, the
193 parameter. For example, with the initial minused = 1000, the
194 dictionary created has size 1024. Of course in a lot of cases that
194 dictionary created has size 1024. Of course in a lot of cases that
195 can be greater than the maximum load factor Python's dict object
195 can be greater than the maximum load factor Python's dict object
196 expects (= 2/3), so as soon as we cross the threshold we'll resize
196 expects (= 2/3), so as soon as we cross the threshold we'll resize
197 anyway. So create a dictionary that's at least 3/2 the size. */
197 anyway. So create a dictionary that's at least 3/2 the size. */
198 return _PyDict_NewPresized(((1 + expected_size) / 2) * 3);
198 return _PyDict_NewPresized(((1 + expected_size) / 2) * 3);
199 }
199 }
200
200
201 static PyObject *dict_new_presized(PyObject *self, PyObject *args)
201 static PyObject *dict_new_presized(PyObject *self, PyObject *args)
202 {
202 {
203 Py_ssize_t expected_size;
203 Py_ssize_t expected_size;
204
204
205 if (!PyArg_ParseTuple(args, "n:make_presized_dict", &expected_size))
205 if (!PyArg_ParseTuple(args, "n:make_presized_dict", &expected_size))
206 return NULL;
206 return NULL;
207
207
208 return _dict_new_presized(expected_size);
208 return _dict_new_presized(expected_size);
209 }
209 }
210
210
211 static PyObject *make_file_foldmap(PyObject *self, PyObject *args)
211 static PyObject *make_file_foldmap(PyObject *self, PyObject *args)
212 {
212 {
213 PyObject *dmap, *spec_obj, *normcase_fallback;
213 PyObject *dmap, *spec_obj, *normcase_fallback;
214 PyObject *file_foldmap = NULL;
214 PyObject *file_foldmap = NULL;
215 enum normcase_spec spec;
215 enum normcase_spec spec;
216 PyObject *k, *v;
216 PyObject *k, *v;
217 dirstateTupleObject *tuple;
217 dirstateTupleObject *tuple;
218 Py_ssize_t pos = 0;
218 Py_ssize_t pos = 0;
219 const char *table;
219 const char *table;
220
220
221 if (!PyArg_ParseTuple(args, "O!O!O!:make_file_foldmap",
221 if (!PyArg_ParseTuple(args, "O!O!O!:make_file_foldmap",
222 &PyDict_Type, &dmap,
222 &PyDict_Type, &dmap,
223 &PyInt_Type, &spec_obj,
223 &PyInt_Type, &spec_obj,
224 &PyFunction_Type, &normcase_fallback))
224 &PyFunction_Type, &normcase_fallback))
225 goto quit;
225 goto quit;
226
226
227 spec = (int)PyInt_AS_LONG(spec_obj);
227 spec = (int)PyInt_AS_LONG(spec_obj);
228 switch (spec) {
228 switch (spec) {
229 case NORMCASE_LOWER:
229 case NORMCASE_LOWER:
230 table = lowertable;
230 table = lowertable;
231 break;
231 break;
232 case NORMCASE_UPPER:
232 case NORMCASE_UPPER:
233 table = uppertable;
233 table = uppertable;
234 break;
234 break;
235 case NORMCASE_OTHER:
235 case NORMCASE_OTHER:
236 table = NULL;
236 table = NULL;
237 break;
237 break;
238 default:
238 default:
239 PyErr_SetString(PyExc_TypeError, "invalid normcasespec");
239 PyErr_SetString(PyExc_TypeError, "invalid normcasespec");
240 goto quit;
240 goto quit;
241 }
241 }
242
242
243 /* Add some more entries to deal with additions outside this
243 /* Add some more entries to deal with additions outside this
244 function. */
244 function. */
245 file_foldmap = _dict_new_presized((PyDict_Size(dmap) / 10) * 11);
245 file_foldmap = _dict_new_presized((PyDict_Size(dmap) / 10) * 11);
246 if (file_foldmap == NULL)
246 if (file_foldmap == NULL)
247 goto quit;
247 goto quit;
248
248
249 while (PyDict_Next(dmap, &pos, &k, &v)) {
249 while (PyDict_Next(dmap, &pos, &k, &v)) {
250 if (!dirstate_tuple_check(v)) {
250 if (!dirstate_tuple_check(v)) {
251 PyErr_SetString(PyExc_TypeError,
251 PyErr_SetString(PyExc_TypeError,
252 "expected a dirstate tuple");
252 "expected a dirstate tuple");
253 goto quit;
253 goto quit;
254 }
254 }
255
255
256 tuple = (dirstateTupleObject *)v;
256 tuple = (dirstateTupleObject *)v;
257 if (tuple->state != 'r') {
257 if (tuple->state != 'r') {
258 PyObject *normed;
258 PyObject *normed;
259 if (table != NULL) {
259 if (table != NULL) {
260 normed = _asciitransform(k, table,
260 normed = _asciitransform(k, table,
261 normcase_fallback);
261 normcase_fallback);
262 } else {
262 } else {
263 normed = PyObject_CallFunctionObjArgs(
263 normed = PyObject_CallFunctionObjArgs(
264 normcase_fallback, k, NULL);
264 normcase_fallback, k, NULL);
265 }
265 }
266
266
267 if (normed == NULL)
267 if (normed == NULL)
268 goto quit;
268 goto quit;
269 if (PyDict_SetItem(file_foldmap, normed, k) == -1) {
269 if (PyDict_SetItem(file_foldmap, normed, k) == -1) {
270 Py_DECREF(normed);
270 Py_DECREF(normed);
271 goto quit;
271 goto quit;
272 }
272 }
273 Py_DECREF(normed);
273 Py_DECREF(normed);
274 }
274 }
275 }
275 }
276 return file_foldmap;
276 return file_foldmap;
277 quit:
277 quit:
278 Py_XDECREF(file_foldmap);
278 Py_XDECREF(file_foldmap);
279 return NULL;
279 return NULL;
280 }
280 }
281
281
282 /*
282 /*
283 * This code assumes that a manifest is stitched together with newline
283 * This code assumes that a manifest is stitched together with newline
284 * ('\n') characters.
284 * ('\n') characters.
285 */
285 */
286 static PyObject *parse_manifest(PyObject *self, PyObject *args)
286 static PyObject *parse_manifest(PyObject *self, PyObject *args)
287 {
287 {
288 PyObject *mfdict, *fdict;
288 PyObject *mfdict, *fdict;
289 char *str, *start, *end;
289 char *str, *start, *end;
290 int len;
290 int len;
291
291
292 if (!PyArg_ParseTuple(args, "O!O!s#:parse_manifest",
292 if (!PyArg_ParseTuple(args, "O!O!s#:parse_manifest",
293 &PyDict_Type, &mfdict,
293 &PyDict_Type, &mfdict,
294 &PyDict_Type, &fdict,
294 &PyDict_Type, &fdict,
295 &str, &len))
295 &str, &len))
296 goto quit;
296 goto quit;
297
297
298 start = str;
298 start = str;
299 end = str + len;
299 end = str + len;
300 while (start < end) {
300 while (start < end) {
301 PyObject *file = NULL, *node = NULL;
301 PyObject *file = NULL, *node = NULL;
302 PyObject *flags = NULL;
302 PyObject *flags = NULL;
303 char *zero = NULL, *newline = NULL;
303 char *zero = NULL, *newline = NULL;
304 ptrdiff_t nlen;
304 ptrdiff_t nlen;
305
305
306 zero = memchr(start, '\0', end - start);
306 zero = memchr(start, '\0', end - start);
307 if (!zero) {
307 if (!zero) {
308 PyErr_SetString(PyExc_ValueError,
308 PyErr_SetString(PyExc_ValueError,
309 "manifest entry has no separator");
309 "manifest entry has no separator");
310 goto quit;
310 goto quit;
311 }
311 }
312
312
313 newline = memchr(zero + 1, '\n', end - (zero + 1));
313 newline = memchr(zero + 1, '\n', end - (zero + 1));
314 if (!newline) {
314 if (!newline) {
315 PyErr_SetString(PyExc_ValueError,
315 PyErr_SetString(PyExc_ValueError,
316 "manifest contains trailing garbage");
316 "manifest contains trailing garbage");
317 goto quit;
317 goto quit;
318 }
318 }
319
319
320 file = PyBytes_FromStringAndSize(start, zero - start);
320 file = PyBytes_FromStringAndSize(start, zero - start);
321
321
322 if (!file)
322 if (!file)
323 goto bail;
323 goto bail;
324
324
325 nlen = newline - zero - 1;
325 nlen = newline - zero - 1;
326
326
327 node = unhexlify(zero + 1, nlen > 40 ? 40 : (int)nlen);
327 node = unhexlify(zero + 1, nlen > 40 ? 40 : (int)nlen);
328 if (!node)
328 if (!node)
329 goto bail;
329 goto bail;
330
330
331 if (nlen > 40) {
331 if (nlen > 40) {
332 flags = PyBytes_FromStringAndSize(zero + 41,
332 flags = PyBytes_FromStringAndSize(zero + 41,
333 nlen - 40);
333 nlen - 40);
334 if (!flags)
334 if (!flags)
335 goto bail;
335 goto bail;
336
336
337 if (PyDict_SetItem(fdict, file, flags) == -1)
337 if (PyDict_SetItem(fdict, file, flags) == -1)
338 goto bail;
338 goto bail;
339 }
339 }
340
340
341 if (PyDict_SetItem(mfdict, file, node) == -1)
341 if (PyDict_SetItem(mfdict, file, node) == -1)
342 goto bail;
342 goto bail;
343
343
344 start = newline + 1;
344 start = newline + 1;
345
345
346 Py_XDECREF(flags);
346 Py_XDECREF(flags);
347 Py_XDECREF(node);
347 Py_XDECREF(node);
348 Py_XDECREF(file);
348 Py_XDECREF(file);
349 continue;
349 continue;
350 bail:
350 bail:
351 Py_XDECREF(flags);
351 Py_XDECREF(flags);
352 Py_XDECREF(node);
352 Py_XDECREF(node);
353 Py_XDECREF(file);
353 Py_XDECREF(file);
354 goto quit;
354 goto quit;
355 }
355 }
356
356
357 Py_INCREF(Py_None);
357 Py_INCREF(Py_None);
358 return Py_None;
358 return Py_None;
359 quit:
359 quit:
360 return NULL;
360 return NULL;
361 }
361 }
362
362
363 static inline dirstateTupleObject *make_dirstate_tuple(char state, int mode,
363 static inline dirstateTupleObject *make_dirstate_tuple(char state, int mode,
364 int size, int mtime)
364 int size, int mtime)
365 {
365 {
366 dirstateTupleObject *t = PyObject_New(dirstateTupleObject,
366 dirstateTupleObject *t = PyObject_New(dirstateTupleObject,
367 &dirstateTupleType);
367 &dirstateTupleType);
368 if (!t)
368 if (!t)
369 return NULL;
369 return NULL;
370 t->state = state;
370 t->state = state;
371 t->mode = mode;
371 t->mode = mode;
372 t->size = size;
372 t->size = size;
373 t->mtime = mtime;
373 t->mtime = mtime;
374 return t;
374 return t;
375 }
375 }
376
376
377 static PyObject *dirstate_tuple_new(PyTypeObject *subtype, PyObject *args,
377 static PyObject *dirstate_tuple_new(PyTypeObject *subtype, PyObject *args,
378 PyObject *kwds)
378 PyObject *kwds)
379 {
379 {
380 /* We do all the initialization here and not a tp_init function because
380 /* We do all the initialization here and not a tp_init function because
381 * dirstate_tuple is immutable. */
381 * dirstate_tuple is immutable. */
382 dirstateTupleObject *t;
382 dirstateTupleObject *t;
383 char state;
383 char state;
384 int size, mode, mtime;
384 int size, mode, mtime;
385 if (!PyArg_ParseTuple(args, "ciii", &state, &mode, &size, &mtime))
385 if (!PyArg_ParseTuple(args, "ciii", &state, &mode, &size, &mtime))
386 return NULL;
386 return NULL;
387
387
388 t = (dirstateTupleObject *)subtype->tp_alloc(subtype, 1);
388 t = (dirstateTupleObject *)subtype->tp_alloc(subtype, 1);
389 if (!t)
389 if (!t)
390 return NULL;
390 return NULL;
391 t->state = state;
391 t->state = state;
392 t->mode = mode;
392 t->mode = mode;
393 t->size = size;
393 t->size = size;
394 t->mtime = mtime;
394 t->mtime = mtime;
395
395
396 return (PyObject *)t;
396 return (PyObject *)t;
397 }
397 }
398
398
399 static void dirstate_tuple_dealloc(PyObject *o)
399 static void dirstate_tuple_dealloc(PyObject *o)
400 {
400 {
401 PyObject_Del(o);
401 PyObject_Del(o);
402 }
402 }
403
403
404 static Py_ssize_t dirstate_tuple_length(PyObject *o)
404 static Py_ssize_t dirstate_tuple_length(PyObject *o)
405 {
405 {
406 return 4;
406 return 4;
407 }
407 }
408
408
409 static PyObject *dirstate_tuple_item(PyObject *o, Py_ssize_t i)
409 static PyObject *dirstate_tuple_item(PyObject *o, Py_ssize_t i)
410 {
410 {
411 dirstateTupleObject *t = (dirstateTupleObject *)o;
411 dirstateTupleObject *t = (dirstateTupleObject *)o;
412 switch (i) {
412 switch (i) {
413 case 0:
413 case 0:
414 return PyBytes_FromStringAndSize(&t->state, 1);
414 return PyBytes_FromStringAndSize(&t->state, 1);
415 case 1:
415 case 1:
416 return PyInt_FromLong(t->mode);
416 return PyInt_FromLong(t->mode);
417 case 2:
417 case 2:
418 return PyInt_FromLong(t->size);
418 return PyInt_FromLong(t->size);
419 case 3:
419 case 3:
420 return PyInt_FromLong(t->mtime);
420 return PyInt_FromLong(t->mtime);
421 default:
421 default:
422 PyErr_SetString(PyExc_IndexError, "index out of range");
422 PyErr_SetString(PyExc_IndexError, "index out of range");
423 return NULL;
423 return NULL;
424 }
424 }
425 }
425 }
426
426
427 static PySequenceMethods dirstate_tuple_sq = {
427 static PySequenceMethods dirstate_tuple_sq = {
428 dirstate_tuple_length, /* sq_length */
428 dirstate_tuple_length, /* sq_length */
429 0, /* sq_concat */
429 0, /* sq_concat */
430 0, /* sq_repeat */
430 0, /* sq_repeat */
431 dirstate_tuple_item, /* sq_item */
431 dirstate_tuple_item, /* sq_item */
432 0, /* sq_ass_item */
432 0, /* sq_ass_item */
433 0, /* sq_contains */
433 0, /* sq_contains */
434 0, /* sq_inplace_concat */
434 0, /* sq_inplace_concat */
435 0 /* sq_inplace_repeat */
435 0 /* sq_inplace_repeat */
436 };
436 };
437
437
438 PyTypeObject dirstateTupleType = {
438 PyTypeObject dirstateTupleType = {
439 PyVarObject_HEAD_INIT(NULL, 0)
439 PyVarObject_HEAD_INIT(NULL, 0)
440 "dirstate_tuple", /* tp_name */
440 "dirstate_tuple", /* tp_name */
441 sizeof(dirstateTupleObject),/* tp_basicsize */
441 sizeof(dirstateTupleObject),/* tp_basicsize */
442 0, /* tp_itemsize */
442 0, /* tp_itemsize */
443 (destructor)dirstate_tuple_dealloc, /* tp_dealloc */
443 (destructor)dirstate_tuple_dealloc, /* tp_dealloc */
444 0, /* tp_print */
444 0, /* tp_print */
445 0, /* tp_getattr */
445 0, /* tp_getattr */
446 0, /* tp_setattr */
446 0, /* tp_setattr */
447 0, /* tp_compare */
447 0, /* tp_compare */
448 0, /* tp_repr */
448 0, /* tp_repr */
449 0, /* tp_as_number */
449 0, /* tp_as_number */
450 &dirstate_tuple_sq, /* tp_as_sequence */
450 &dirstate_tuple_sq, /* tp_as_sequence */
451 0, /* tp_as_mapping */
451 0, /* tp_as_mapping */
452 0, /* tp_hash */
452 0, /* tp_hash */
453 0, /* tp_call */
453 0, /* tp_call */
454 0, /* tp_str */
454 0, /* tp_str */
455 0, /* tp_getattro */
455 0, /* tp_getattro */
456 0, /* tp_setattro */
456 0, /* tp_setattro */
457 0, /* tp_as_buffer */
457 0, /* tp_as_buffer */
458 Py_TPFLAGS_DEFAULT, /* tp_flags */
458 Py_TPFLAGS_DEFAULT, /* tp_flags */
459 "dirstate tuple", /* tp_doc */
459 "dirstate tuple", /* tp_doc */
460 0, /* tp_traverse */
460 0, /* tp_traverse */
461 0, /* tp_clear */
461 0, /* tp_clear */
462 0, /* tp_richcompare */
462 0, /* tp_richcompare */
463 0, /* tp_weaklistoffset */
463 0, /* tp_weaklistoffset */
464 0, /* tp_iter */
464 0, /* tp_iter */
465 0, /* tp_iternext */
465 0, /* tp_iternext */
466 0, /* tp_methods */
466 0, /* tp_methods */
467 0, /* tp_members */
467 0, /* tp_members */
468 0, /* tp_getset */
468 0, /* tp_getset */
469 0, /* tp_base */
469 0, /* tp_base */
470 0, /* tp_dict */
470 0, /* tp_dict */
471 0, /* tp_descr_get */
471 0, /* tp_descr_get */
472 0, /* tp_descr_set */
472 0, /* tp_descr_set */
473 0, /* tp_dictoffset */
473 0, /* tp_dictoffset */
474 0, /* tp_init */
474 0, /* tp_init */
475 0, /* tp_alloc */
475 0, /* tp_alloc */
476 dirstate_tuple_new, /* tp_new */
476 dirstate_tuple_new, /* tp_new */
477 };
477 };
478
478
479 static PyObject *parse_dirstate(PyObject *self, PyObject *args)
479 static PyObject *parse_dirstate(PyObject *self, PyObject *args)
480 {
480 {
481 PyObject *dmap, *cmap, *parents = NULL, *ret = NULL;
481 PyObject *dmap, *cmap, *parents = NULL, *ret = NULL;
482 PyObject *fname = NULL, *cname = NULL, *entry = NULL;
482 PyObject *fname = NULL, *cname = NULL, *entry = NULL;
483 char state, *cur, *str, *cpos;
483 char state, *cur, *str, *cpos;
484 int mode, size, mtime;
484 int mode, size, mtime;
485 unsigned int flen, len, pos = 40;
485 unsigned int flen, len, pos = 40;
486 int readlen;
486 int readlen;
487
487
488 if (!PyArg_ParseTuple(args, "O!O!s#:parse_dirstate",
488 if (!PyArg_ParseTuple(args, "O!O!s#:parse_dirstate",
489 &PyDict_Type, &dmap,
489 &PyDict_Type, &dmap,
490 &PyDict_Type, &cmap,
490 &PyDict_Type, &cmap,
491 &str, &readlen))
491 &str, &readlen))
492 goto quit;
492 goto quit;
493
493
494 len = readlen;
494 len = readlen;
495
495
496 /* read parents */
496 /* read parents */
497 if (len < 40) {
497 if (len < 40) {
498 PyErr_SetString(
498 PyErr_SetString(
499 PyExc_ValueError, "too little data for parents");
499 PyExc_ValueError, "too little data for parents");
500 goto quit;
500 goto quit;
501 }
501 }
502
502
503 parents = Py_BuildValue("s#s#", str, 20, str + 20, 20);
503 parents = Py_BuildValue("s#s#", str, 20, str + 20, 20);
504 if (!parents)
504 if (!parents)
505 goto quit;
505 goto quit;
506
506
507 /* read filenames */
507 /* read filenames */
508 while (pos >= 40 && pos < len) {
508 while (pos >= 40 && pos < len) {
509 if (pos + 17 > len) {
509 if (pos + 17 > len) {
510 PyErr_SetString(PyExc_ValueError,
510 PyErr_SetString(PyExc_ValueError,
511 "overflow in dirstate");
511 "overflow in dirstate");
512 goto quit;
512 goto quit;
513 }
513 }
514 cur = str + pos;
514 cur = str + pos;
515 /* unpack header */
515 /* unpack header */
516 state = *cur;
516 state = *cur;
517 mode = getbe32(cur + 1);
517 mode = getbe32(cur + 1);
518 size = getbe32(cur + 5);
518 size = getbe32(cur + 5);
519 mtime = getbe32(cur + 9);
519 mtime = getbe32(cur + 9);
520 flen = getbe32(cur + 13);
520 flen = getbe32(cur + 13);
521 pos += 17;
521 pos += 17;
522 cur += 17;
522 cur += 17;
523 if (flen > len - pos) {
523 if (flen > len - pos) {
524 PyErr_SetString(PyExc_ValueError, "overflow in dirstate");
524 PyErr_SetString(PyExc_ValueError, "overflow in dirstate");
525 goto quit;
525 goto quit;
526 }
526 }
527
527
528 entry = (PyObject *)make_dirstate_tuple(state, mode, size,
528 entry = (PyObject *)make_dirstate_tuple(state, mode, size,
529 mtime);
529 mtime);
530 cpos = memchr(cur, 0, flen);
530 cpos = memchr(cur, 0, flen);
531 if (cpos) {
531 if (cpos) {
532 fname = PyBytes_FromStringAndSize(cur, cpos - cur);
532 fname = PyBytes_FromStringAndSize(cur, cpos - cur);
533 cname = PyBytes_FromStringAndSize(cpos + 1,
533 cname = PyBytes_FromStringAndSize(cpos + 1,
534 flen - (cpos - cur) - 1);
534 flen - (cpos - cur) - 1);
535 if (!fname || !cname ||
535 if (!fname || !cname ||
536 PyDict_SetItem(cmap, fname, cname) == -1 ||
536 PyDict_SetItem(cmap, fname, cname) == -1 ||
537 PyDict_SetItem(dmap, fname, entry) == -1)
537 PyDict_SetItem(dmap, fname, entry) == -1)
538 goto quit;
538 goto quit;
539 Py_DECREF(cname);
539 Py_DECREF(cname);
540 } else {
540 } else {
541 fname = PyBytes_FromStringAndSize(cur, flen);
541 fname = PyBytes_FromStringAndSize(cur, flen);
542 if (!fname ||
542 if (!fname ||
543 PyDict_SetItem(dmap, fname, entry) == -1)
543 PyDict_SetItem(dmap, fname, entry) == -1)
544 goto quit;
544 goto quit;
545 }
545 }
546 Py_DECREF(fname);
546 Py_DECREF(fname);
547 Py_DECREF(entry);
547 Py_DECREF(entry);
548 fname = cname = entry = NULL;
548 fname = cname = entry = NULL;
549 pos += flen;
549 pos += flen;
550 }
550 }
551
551
552 ret = parents;
552 ret = parents;
553 Py_INCREF(ret);
553 Py_INCREF(ret);
554 quit:
554 quit:
555 Py_XDECREF(fname);
555 Py_XDECREF(fname);
556 Py_XDECREF(cname);
556 Py_XDECREF(cname);
557 Py_XDECREF(entry);
557 Py_XDECREF(entry);
558 Py_XDECREF(parents);
558 Py_XDECREF(parents);
559 return ret;
559 return ret;
560 }
560 }
561
561
562 /*
562 /*
563 * Build a set of non-normal entries from the dirstate dmap
563 * Build a set of non-normal and other parent entries from the dirstate dmap
564 */
564 */
565 static PyObject *nonnormalentries(PyObject *self, PyObject *args)
565 static PyObject *nonnormalotherparententries(PyObject *self, PyObject *args) {
566 {
566 PyObject *dmap, *fname, *v;
567 PyObject *dmap, *nonnset = NULL, *fname, *v;
567 PyObject *nonnset = NULL, *otherpset = NULL, *result = NULL;
568 Py_ssize_t pos;
568 Py_ssize_t pos;
569
569
570 if (!PyArg_ParseTuple(args, "O!:nonnormalentries",
570 if (!PyArg_ParseTuple(args, "O!:nonnormalentries",
571 &PyDict_Type, &dmap))
571 &PyDict_Type, &dmap))
572 goto bail;
572 goto bail;
573
573
574 nonnset = PySet_New(NULL);
574 nonnset = PySet_New(NULL);
575 if (nonnset == NULL)
575 if (nonnset == NULL)
576 goto bail;
576 goto bail;
577
577
578 otherpset = PySet_New(NULL);
579 if (otherpset == NULL)
580 goto bail;
581
578 pos = 0;
582 pos = 0;
579 while (PyDict_Next(dmap, &pos, &fname, &v)) {
583 while (PyDict_Next(dmap, &pos, &fname, &v)) {
580 dirstateTupleObject *t;
584 dirstateTupleObject *t;
581 if (!dirstate_tuple_check(v)) {
585 if (!dirstate_tuple_check(v)) {
582 PyErr_SetString(PyExc_TypeError,
586 PyErr_SetString(PyExc_TypeError,
583 "expected a dirstate tuple");
587 "expected a dirstate tuple");
584 goto bail;
588 goto bail;
585 }
589 }
586 t = (dirstateTupleObject *)v;
590 t = (dirstateTupleObject *)v;
587
591
592 if (t->state == 'n' && t->size == -2) {
593 if (PySet_Add(otherpset, fname) == -1) {
594 goto bail;
595 }
596 }
597
588 if (t->state == 'n' && t->mtime != -1)
598 if (t->state == 'n' && t->mtime != -1)
589 continue;
599 continue;
590 if (PySet_Add(nonnset, fname) == -1)
600 if (PySet_Add(nonnset, fname) == -1)
591 goto bail;
601 goto bail;
592 }
602 }
593
603
594 return nonnset;
604 result = Py_BuildValue("(OO)", nonnset, otherpset);
605 if (result == NULL)
606 goto bail;
607 return result;
595 bail:
608 bail:
596 Py_XDECREF(nonnset);
609 Py_XDECREF(nonnset);
610 Py_XDECREF(otherpset);
611 Py_XDECREF(result);
597 return NULL;
612 return NULL;
598 }
613 }
599
614
600 /*
615 /*
616 * Build a set of non-normal entries from the dirstate dmap
617 */
618 static PyObject *nonnormalentries(PyObject *self, PyObject *args)
619 {
620 PyObject *nonnset = NULL, *combined = NULL;
621
622 combined = nonnormalotherparententries(self, args);
623 if (!combined) {
624 return NULL;
625 }
626
627 nonnset = PyTuple_GetItem(combined, 0);
628 if (!nonnset) {
629 Py_DECREF(combined);
630 return NULL;
631 }
632
633 Py_INCREF(nonnset);
634 Py_DECREF(combined);
635 return nonnset;
636 }
637
638 /*
601 * Efficiently pack a dirstate object into its on-disk format.
639 * Efficiently pack a dirstate object into its on-disk format.
602 */
640 */
603 static PyObject *pack_dirstate(PyObject *self, PyObject *args)
641 static PyObject *pack_dirstate(PyObject *self, PyObject *args)
604 {
642 {
605 PyObject *packobj = NULL;
643 PyObject *packobj = NULL;
606 PyObject *map, *copymap, *pl, *mtime_unset = NULL;
644 PyObject *map, *copymap, *pl, *mtime_unset = NULL;
607 Py_ssize_t nbytes, pos, l;
645 Py_ssize_t nbytes, pos, l;
608 PyObject *k, *v = NULL, *pn;
646 PyObject *k, *v = NULL, *pn;
609 char *p, *s;
647 char *p, *s;
610 int now;
648 int now;
611
649
612 if (!PyArg_ParseTuple(args, "O!O!Oi:pack_dirstate",
650 if (!PyArg_ParseTuple(args, "O!O!Oi:pack_dirstate",
613 &PyDict_Type, &map, &PyDict_Type, &copymap,
651 &PyDict_Type, &map, &PyDict_Type, &copymap,
614 &pl, &now))
652 &pl, &now))
615 return NULL;
653 return NULL;
616
654
617 if (!PySequence_Check(pl) || PySequence_Size(pl) != 2) {
655 if (!PySequence_Check(pl) || PySequence_Size(pl) != 2) {
618 PyErr_SetString(PyExc_TypeError, "expected 2-element sequence");
656 PyErr_SetString(PyExc_TypeError, "expected 2-element sequence");
619 return NULL;
657 return NULL;
620 }
658 }
621
659
622 /* Figure out how much we need to allocate. */
660 /* Figure out how much we need to allocate. */
623 for (nbytes = 40, pos = 0; PyDict_Next(map, &pos, &k, &v);) {
661 for (nbytes = 40, pos = 0; PyDict_Next(map, &pos, &k, &v);) {
624 PyObject *c;
662 PyObject *c;
625 if (!PyBytes_Check(k)) {
663 if (!PyBytes_Check(k)) {
626 PyErr_SetString(PyExc_TypeError, "expected string key");
664 PyErr_SetString(PyExc_TypeError, "expected string key");
627 goto bail;
665 goto bail;
628 }
666 }
629 nbytes += PyBytes_GET_SIZE(k) + 17;
667 nbytes += PyBytes_GET_SIZE(k) + 17;
630 c = PyDict_GetItem(copymap, k);
668 c = PyDict_GetItem(copymap, k);
631 if (c) {
669 if (c) {
632 if (!PyBytes_Check(c)) {
670 if (!PyBytes_Check(c)) {
633 PyErr_SetString(PyExc_TypeError,
671 PyErr_SetString(PyExc_TypeError,
634 "expected string key");
672 "expected string key");
635 goto bail;
673 goto bail;
636 }
674 }
637 nbytes += PyBytes_GET_SIZE(c) + 1;
675 nbytes += PyBytes_GET_SIZE(c) + 1;
638 }
676 }
639 }
677 }
640
678
641 packobj = PyBytes_FromStringAndSize(NULL, nbytes);
679 packobj = PyBytes_FromStringAndSize(NULL, nbytes);
642 if (packobj == NULL)
680 if (packobj == NULL)
643 goto bail;
681 goto bail;
644
682
645 p = PyBytes_AS_STRING(packobj);
683 p = PyBytes_AS_STRING(packobj);
646
684
647 pn = PySequence_ITEM(pl, 0);
685 pn = PySequence_ITEM(pl, 0);
648 if (PyBytes_AsStringAndSize(pn, &s, &l) == -1 || l != 20) {
686 if (PyBytes_AsStringAndSize(pn, &s, &l) == -1 || l != 20) {
649 PyErr_SetString(PyExc_TypeError, "expected a 20-byte hash");
687 PyErr_SetString(PyExc_TypeError, "expected a 20-byte hash");
650 goto bail;
688 goto bail;
651 }
689 }
652 memcpy(p, s, l);
690 memcpy(p, s, l);
653 p += 20;
691 p += 20;
654 pn = PySequence_ITEM(pl, 1);
692 pn = PySequence_ITEM(pl, 1);
655 if (PyBytes_AsStringAndSize(pn, &s, &l) == -1 || l != 20) {
693 if (PyBytes_AsStringAndSize(pn, &s, &l) == -1 || l != 20) {
656 PyErr_SetString(PyExc_TypeError, "expected a 20-byte hash");
694 PyErr_SetString(PyExc_TypeError, "expected a 20-byte hash");
657 goto bail;
695 goto bail;
658 }
696 }
659 memcpy(p, s, l);
697 memcpy(p, s, l);
660 p += 20;
698 p += 20;
661
699
662 for (pos = 0; PyDict_Next(map, &pos, &k, &v); ) {
700 for (pos = 0; PyDict_Next(map, &pos, &k, &v); ) {
663 dirstateTupleObject *tuple;
701 dirstateTupleObject *tuple;
664 char state;
702 char state;
665 int mode, size, mtime;
703 int mode, size, mtime;
666 Py_ssize_t len, l;
704 Py_ssize_t len, l;
667 PyObject *o;
705 PyObject *o;
668 char *t;
706 char *t;
669
707
670 if (!dirstate_tuple_check(v)) {
708 if (!dirstate_tuple_check(v)) {
671 PyErr_SetString(PyExc_TypeError,
709 PyErr_SetString(PyExc_TypeError,
672 "expected a dirstate tuple");
710 "expected a dirstate tuple");
673 goto bail;
711 goto bail;
674 }
712 }
675 tuple = (dirstateTupleObject *)v;
713 tuple = (dirstateTupleObject *)v;
676
714
677 state = tuple->state;
715 state = tuple->state;
678 mode = tuple->mode;
716 mode = tuple->mode;
679 size = tuple->size;
717 size = tuple->size;
680 mtime = tuple->mtime;
718 mtime = tuple->mtime;
681 if (state == 'n' && mtime == now) {
719 if (state == 'n' && mtime == now) {
682 /* See pure/parsers.py:pack_dirstate for why we do
720 /* See pure/parsers.py:pack_dirstate for why we do
683 * this. */
721 * this. */
684 mtime = -1;
722 mtime = -1;
685 mtime_unset = (PyObject *)make_dirstate_tuple(
723 mtime_unset = (PyObject *)make_dirstate_tuple(
686 state, mode, size, mtime);
724 state, mode, size, mtime);
687 if (!mtime_unset)
725 if (!mtime_unset)
688 goto bail;
726 goto bail;
689 if (PyDict_SetItem(map, k, mtime_unset) == -1)
727 if (PyDict_SetItem(map, k, mtime_unset) == -1)
690 goto bail;
728 goto bail;
691 Py_DECREF(mtime_unset);
729 Py_DECREF(mtime_unset);
692 mtime_unset = NULL;
730 mtime_unset = NULL;
693 }
731 }
694 *p++ = state;
732 *p++ = state;
695 putbe32((uint32_t)mode, p);
733 putbe32((uint32_t)mode, p);
696 putbe32((uint32_t)size, p + 4);
734 putbe32((uint32_t)size, p + 4);
697 putbe32((uint32_t)mtime, p + 8);
735 putbe32((uint32_t)mtime, p + 8);
698 t = p + 12;
736 t = p + 12;
699 p += 16;
737 p += 16;
700 len = PyBytes_GET_SIZE(k);
738 len = PyBytes_GET_SIZE(k);
701 memcpy(p, PyBytes_AS_STRING(k), len);
739 memcpy(p, PyBytes_AS_STRING(k), len);
702 p += len;
740 p += len;
703 o = PyDict_GetItem(copymap, k);
741 o = PyDict_GetItem(copymap, k);
704 if (o) {
742 if (o) {
705 *p++ = '\0';
743 *p++ = '\0';
706 l = PyBytes_GET_SIZE(o);
744 l = PyBytes_GET_SIZE(o);
707 memcpy(p, PyBytes_AS_STRING(o), l);
745 memcpy(p, PyBytes_AS_STRING(o), l);
708 p += l;
746 p += l;
709 len += l + 1;
747 len += l + 1;
710 }
748 }
711 putbe32((uint32_t)len, t);
749 putbe32((uint32_t)len, t);
712 }
750 }
713
751
714 pos = p - PyBytes_AS_STRING(packobj);
752 pos = p - PyBytes_AS_STRING(packobj);
715 if (pos != nbytes) {
753 if (pos != nbytes) {
716 PyErr_Format(PyExc_SystemError, "bad dirstate size: %ld != %ld",
754 PyErr_Format(PyExc_SystemError, "bad dirstate size: %ld != %ld",
717 (long)pos, (long)nbytes);
755 (long)pos, (long)nbytes);
718 goto bail;
756 goto bail;
719 }
757 }
720
758
721 return packobj;
759 return packobj;
722 bail:
760 bail:
723 Py_XDECREF(mtime_unset);
761 Py_XDECREF(mtime_unset);
724 Py_XDECREF(packobj);
762 Py_XDECREF(packobj);
725 Py_XDECREF(v);
763 Py_XDECREF(v);
726 return NULL;
764 return NULL;
727 }
765 }
728
766
729 /*
767 /*
730 * A base-16 trie for fast node->rev mapping.
768 * A base-16 trie for fast node->rev mapping.
731 *
769 *
732 * Positive value is index of the next node in the trie
770 * Positive value is index of the next node in the trie
733 * Negative value is a leaf: -(rev + 1)
771 * Negative value is a leaf: -(rev + 1)
734 * Zero is empty
772 * Zero is empty
735 */
773 */
736 typedef struct {
774 typedef struct {
737 int children[16];
775 int children[16];
738 } nodetree;
776 } nodetree;
739
777
740 /*
778 /*
741 * This class has two behaviors.
779 * This class has two behaviors.
742 *
780 *
743 * When used in a list-like way (with integer keys), we decode an
781 * When used in a list-like way (with integer keys), we decode an
744 * entry in a RevlogNG index file on demand. Our last entry is a
782 * entry in a RevlogNG index file on demand. Our last entry is a
745 * sentinel, always a nullid. We have limited support for
783 * sentinel, always a nullid. We have limited support for
746 * integer-keyed insert and delete, only at elements right before the
784 * integer-keyed insert and delete, only at elements right before the
747 * sentinel.
785 * sentinel.
748 *
786 *
749 * With string keys, we lazily perform a reverse mapping from node to
787 * With string keys, we lazily perform a reverse mapping from node to
750 * rev, using a base-16 trie.
788 * rev, using a base-16 trie.
751 */
789 */
752 typedef struct {
790 typedef struct {
753 PyObject_HEAD
791 PyObject_HEAD
754 /* Type-specific fields go here. */
792 /* Type-specific fields go here. */
755 PyObject *data; /* raw bytes of index */
793 PyObject *data; /* raw bytes of index */
756 Py_buffer buf; /* buffer of data */
794 Py_buffer buf; /* buffer of data */
757 PyObject **cache; /* cached tuples */
795 PyObject **cache; /* cached tuples */
758 const char **offsets; /* populated on demand */
796 const char **offsets; /* populated on demand */
759 Py_ssize_t raw_length; /* original number of elements */
797 Py_ssize_t raw_length; /* original number of elements */
760 Py_ssize_t length; /* current number of elements */
798 Py_ssize_t length; /* current number of elements */
761 PyObject *added; /* populated on demand */
799 PyObject *added; /* populated on demand */
762 PyObject *headrevs; /* cache, invalidated on changes */
800 PyObject *headrevs; /* cache, invalidated on changes */
763 PyObject *filteredrevs;/* filtered revs set */
801 PyObject *filteredrevs;/* filtered revs set */
764 nodetree *nt; /* base-16 trie */
802 nodetree *nt; /* base-16 trie */
765 unsigned ntlength; /* # nodes in use */
803 unsigned ntlength; /* # nodes in use */
766 unsigned ntcapacity; /* # nodes allocated */
804 unsigned ntcapacity; /* # nodes allocated */
767 int ntdepth; /* maximum depth of tree */
805 int ntdepth; /* maximum depth of tree */
768 int ntsplits; /* # splits performed */
806 int ntsplits; /* # splits performed */
769 int ntrev; /* last rev scanned */
807 int ntrev; /* last rev scanned */
770 int ntlookups; /* # lookups */
808 int ntlookups; /* # lookups */
771 int ntmisses; /* # lookups that miss the cache */
809 int ntmisses; /* # lookups that miss the cache */
772 int inlined;
810 int inlined;
773 } indexObject;
811 } indexObject;
774
812
775 static Py_ssize_t index_length(const indexObject *self)
813 static Py_ssize_t index_length(const indexObject *self)
776 {
814 {
777 if (self->added == NULL)
815 if (self->added == NULL)
778 return self->length;
816 return self->length;
779 return self->length + PyList_GET_SIZE(self->added);
817 return self->length + PyList_GET_SIZE(self->added);
780 }
818 }
781
819
782 static PyObject *nullentry;
820 static PyObject *nullentry;
783 static const char nullid[20];
821 static const char nullid[20];
784
822
785 static Py_ssize_t inline_scan(indexObject *self, const char **offsets);
823 static Py_ssize_t inline_scan(indexObject *self, const char **offsets);
786
824
787 #if LONG_MAX == 0x7fffffffL
825 #if LONG_MAX == 0x7fffffffL
788 static char *tuple_format = "Kiiiiiis#";
826 static char *tuple_format = "Kiiiiiis#";
789 #else
827 #else
790 static char *tuple_format = "kiiiiiis#";
828 static char *tuple_format = "kiiiiiis#";
791 #endif
829 #endif
792
830
793 /* A RevlogNG v1 index entry is 64 bytes long. */
831 /* A RevlogNG v1 index entry is 64 bytes long. */
794 static const long v1_hdrsize = 64;
832 static const long v1_hdrsize = 64;
795
833
796 /*
834 /*
797 * Return a pointer to the beginning of a RevlogNG record.
835 * Return a pointer to the beginning of a RevlogNG record.
798 */
836 */
799 static const char *index_deref(indexObject *self, Py_ssize_t pos)
837 static const char *index_deref(indexObject *self, Py_ssize_t pos)
800 {
838 {
801 if (self->inlined && pos > 0) {
839 if (self->inlined && pos > 0) {
802 if (self->offsets == NULL) {
840 if (self->offsets == NULL) {
803 self->offsets = malloc(self->raw_length *
841 self->offsets = malloc(self->raw_length *
804 sizeof(*self->offsets));
842 sizeof(*self->offsets));
805 if (self->offsets == NULL)
843 if (self->offsets == NULL)
806 return (const char *)PyErr_NoMemory();
844 return (const char *)PyErr_NoMemory();
807 inline_scan(self, self->offsets);
845 inline_scan(self, self->offsets);
808 }
846 }
809 return self->offsets[pos];
847 return self->offsets[pos];
810 }
848 }
811
849
812 return (const char *)(self->buf.buf) + pos * v1_hdrsize;
850 return (const char *)(self->buf.buf) + pos * v1_hdrsize;
813 }
851 }
814
852
815 static inline int index_get_parents(indexObject *self, Py_ssize_t rev,
853 static inline int index_get_parents(indexObject *self, Py_ssize_t rev,
816 int *ps, int maxrev)
854 int *ps, int maxrev)
817 {
855 {
818 if (rev >= self->length - 1) {
856 if (rev >= self->length - 1) {
819 PyObject *tuple = PyList_GET_ITEM(self->added,
857 PyObject *tuple = PyList_GET_ITEM(self->added,
820 rev - self->length + 1);
858 rev - self->length + 1);
821 ps[0] = (int)PyInt_AS_LONG(PyTuple_GET_ITEM(tuple, 5));
859 ps[0] = (int)PyInt_AS_LONG(PyTuple_GET_ITEM(tuple, 5));
822 ps[1] = (int)PyInt_AS_LONG(PyTuple_GET_ITEM(tuple, 6));
860 ps[1] = (int)PyInt_AS_LONG(PyTuple_GET_ITEM(tuple, 6));
823 } else {
861 } else {
824 const char *data = index_deref(self, rev);
862 const char *data = index_deref(self, rev);
825 ps[0] = getbe32(data + 24);
863 ps[0] = getbe32(data + 24);
826 ps[1] = getbe32(data + 28);
864 ps[1] = getbe32(data + 28);
827 }
865 }
828 /* If index file is corrupted, ps[] may point to invalid revisions. So
866 /* If index file is corrupted, ps[] may point to invalid revisions. So
829 * there is a risk of buffer overflow to trust them unconditionally. */
867 * there is a risk of buffer overflow to trust them unconditionally. */
830 if (ps[0] > maxrev || ps[1] > maxrev) {
868 if (ps[0] > maxrev || ps[1] > maxrev) {
831 PyErr_SetString(PyExc_ValueError, "parent out of range");
869 PyErr_SetString(PyExc_ValueError, "parent out of range");
832 return -1;
870 return -1;
833 }
871 }
834 return 0;
872 return 0;
835 }
873 }
836
874
837
875
838 /*
876 /*
839 * RevlogNG format (all in big endian, data may be inlined):
877 * RevlogNG format (all in big endian, data may be inlined):
840 * 6 bytes: offset
878 * 6 bytes: offset
841 * 2 bytes: flags
879 * 2 bytes: flags
842 * 4 bytes: compressed length
880 * 4 bytes: compressed length
843 * 4 bytes: uncompressed length
881 * 4 bytes: uncompressed length
844 * 4 bytes: base revision
882 * 4 bytes: base revision
845 * 4 bytes: link revision
883 * 4 bytes: link revision
846 * 4 bytes: parent 1 revision
884 * 4 bytes: parent 1 revision
847 * 4 bytes: parent 2 revision
885 * 4 bytes: parent 2 revision
848 * 32 bytes: nodeid (only 20 bytes used)
886 * 32 bytes: nodeid (only 20 bytes used)
849 */
887 */
850 static PyObject *index_get(indexObject *self, Py_ssize_t pos)
888 static PyObject *index_get(indexObject *self, Py_ssize_t pos)
851 {
889 {
852 uint64_t offset_flags;
890 uint64_t offset_flags;
853 int comp_len, uncomp_len, base_rev, link_rev, parent_1, parent_2;
891 int comp_len, uncomp_len, base_rev, link_rev, parent_1, parent_2;
854 const char *c_node_id;
892 const char *c_node_id;
855 const char *data;
893 const char *data;
856 Py_ssize_t length = index_length(self);
894 Py_ssize_t length = index_length(self);
857 PyObject *entry;
895 PyObject *entry;
858
896
859 if (pos < 0)
897 if (pos < 0)
860 pos += length;
898 pos += length;
861
899
862 if (pos < 0 || pos >= length) {
900 if (pos < 0 || pos >= length) {
863 PyErr_SetString(PyExc_IndexError, "revlog index out of range");
901 PyErr_SetString(PyExc_IndexError, "revlog index out of range");
864 return NULL;
902 return NULL;
865 }
903 }
866
904
867 if (pos == length - 1) {
905 if (pos == length - 1) {
868 Py_INCREF(nullentry);
906 Py_INCREF(nullentry);
869 return nullentry;
907 return nullentry;
870 }
908 }
871
909
872 if (pos >= self->length - 1) {
910 if (pos >= self->length - 1) {
873 PyObject *obj;
911 PyObject *obj;
874 obj = PyList_GET_ITEM(self->added, pos - self->length + 1);
912 obj = PyList_GET_ITEM(self->added, pos - self->length + 1);
875 Py_INCREF(obj);
913 Py_INCREF(obj);
876 return obj;
914 return obj;
877 }
915 }
878
916
879 if (self->cache) {
917 if (self->cache) {
880 if (self->cache[pos]) {
918 if (self->cache[pos]) {
881 Py_INCREF(self->cache[pos]);
919 Py_INCREF(self->cache[pos]);
882 return self->cache[pos];
920 return self->cache[pos];
883 }
921 }
884 } else {
922 } else {
885 self->cache = calloc(self->raw_length, sizeof(PyObject *));
923 self->cache = calloc(self->raw_length, sizeof(PyObject *));
886 if (self->cache == NULL)
924 if (self->cache == NULL)
887 return PyErr_NoMemory();
925 return PyErr_NoMemory();
888 }
926 }
889
927
890 data = index_deref(self, pos);
928 data = index_deref(self, pos);
891 if (data == NULL)
929 if (data == NULL)
892 return NULL;
930 return NULL;
893
931
894 offset_flags = getbe32(data + 4);
932 offset_flags = getbe32(data + 4);
895 if (pos == 0) /* mask out version number for the first entry */
933 if (pos == 0) /* mask out version number for the first entry */
896 offset_flags &= 0xFFFF;
934 offset_flags &= 0xFFFF;
897 else {
935 else {
898 uint32_t offset_high = getbe32(data);
936 uint32_t offset_high = getbe32(data);
899 offset_flags |= ((uint64_t)offset_high) << 32;
937 offset_flags |= ((uint64_t)offset_high) << 32;
900 }
938 }
901
939
902 comp_len = getbe32(data + 8);
940 comp_len = getbe32(data + 8);
903 uncomp_len = getbe32(data + 12);
941 uncomp_len = getbe32(data + 12);
904 base_rev = getbe32(data + 16);
942 base_rev = getbe32(data + 16);
905 link_rev = getbe32(data + 20);
943 link_rev = getbe32(data + 20);
906 parent_1 = getbe32(data + 24);
944 parent_1 = getbe32(data + 24);
907 parent_2 = getbe32(data + 28);
945 parent_2 = getbe32(data + 28);
908 c_node_id = data + 32;
946 c_node_id = data + 32;
909
947
910 entry = Py_BuildValue(tuple_format, offset_flags, comp_len,
948 entry = Py_BuildValue(tuple_format, offset_flags, comp_len,
911 uncomp_len, base_rev, link_rev,
949 uncomp_len, base_rev, link_rev,
912 parent_1, parent_2, c_node_id, 20);
950 parent_1, parent_2, c_node_id, 20);
913
951
914 if (entry) {
952 if (entry) {
915 PyObject_GC_UnTrack(entry);
953 PyObject_GC_UnTrack(entry);
916 Py_INCREF(entry);
954 Py_INCREF(entry);
917 }
955 }
918
956
919 self->cache[pos] = entry;
957 self->cache[pos] = entry;
920
958
921 return entry;
959 return entry;
922 }
960 }
923
961
924 /*
962 /*
925 * Return the 20-byte SHA of the node corresponding to the given rev.
963 * Return the 20-byte SHA of the node corresponding to the given rev.
926 */
964 */
927 static const char *index_node(indexObject *self, Py_ssize_t pos)
965 static const char *index_node(indexObject *self, Py_ssize_t pos)
928 {
966 {
929 Py_ssize_t length = index_length(self);
967 Py_ssize_t length = index_length(self);
930 const char *data;
968 const char *data;
931
969
932 if (pos == length - 1 || pos == INT_MAX)
970 if (pos == length - 1 || pos == INT_MAX)
933 return nullid;
971 return nullid;
934
972
935 if (pos >= length)
973 if (pos >= length)
936 return NULL;
974 return NULL;
937
975
938 if (pos >= self->length - 1) {
976 if (pos >= self->length - 1) {
939 PyObject *tuple, *str;
977 PyObject *tuple, *str;
940 tuple = PyList_GET_ITEM(self->added, pos - self->length + 1);
978 tuple = PyList_GET_ITEM(self->added, pos - self->length + 1);
941 str = PyTuple_GetItem(tuple, 7);
979 str = PyTuple_GetItem(tuple, 7);
942 return str ? PyBytes_AS_STRING(str) : NULL;
980 return str ? PyBytes_AS_STRING(str) : NULL;
943 }
981 }
944
982
945 data = index_deref(self, pos);
983 data = index_deref(self, pos);
946 return data ? data + 32 : NULL;
984 return data ? data + 32 : NULL;
947 }
985 }
948
986
949 static int nt_insert(indexObject *self, const char *node, int rev);
987 static int nt_insert(indexObject *self, const char *node, int rev);
950
988
951 static int node_check(PyObject *obj, char **node, Py_ssize_t *nodelen)
989 static int node_check(PyObject *obj, char **node, Py_ssize_t *nodelen)
952 {
990 {
953 if (PyBytes_AsStringAndSize(obj, node, nodelen) == -1)
991 if (PyBytes_AsStringAndSize(obj, node, nodelen) == -1)
954 return -1;
992 return -1;
955 if (*nodelen == 20)
993 if (*nodelen == 20)
956 return 0;
994 return 0;
957 PyErr_SetString(PyExc_ValueError, "20-byte hash required");
995 PyErr_SetString(PyExc_ValueError, "20-byte hash required");
958 return -1;
996 return -1;
959 }
997 }
960
998
961 static PyObject *index_insert(indexObject *self, PyObject *args)
999 static PyObject *index_insert(indexObject *self, PyObject *args)
962 {
1000 {
963 PyObject *obj;
1001 PyObject *obj;
964 char *node;
1002 char *node;
965 int index;
1003 int index;
966 Py_ssize_t len, nodelen;
1004 Py_ssize_t len, nodelen;
967
1005
968 if (!PyArg_ParseTuple(args, "iO", &index, &obj))
1006 if (!PyArg_ParseTuple(args, "iO", &index, &obj))
969 return NULL;
1007 return NULL;
970
1008
971 if (!PyTuple_Check(obj) || PyTuple_GET_SIZE(obj) != 8) {
1009 if (!PyTuple_Check(obj) || PyTuple_GET_SIZE(obj) != 8) {
972 PyErr_SetString(PyExc_TypeError, "8-tuple required");
1010 PyErr_SetString(PyExc_TypeError, "8-tuple required");
973 return NULL;
1011 return NULL;
974 }
1012 }
975
1013
976 if (node_check(PyTuple_GET_ITEM(obj, 7), &node, &nodelen) == -1)
1014 if (node_check(PyTuple_GET_ITEM(obj, 7), &node, &nodelen) == -1)
977 return NULL;
1015 return NULL;
978
1016
979 len = index_length(self);
1017 len = index_length(self);
980
1018
981 if (index < 0)
1019 if (index < 0)
982 index += len;
1020 index += len;
983
1021
984 if (index != len - 1) {
1022 if (index != len - 1) {
985 PyErr_SetString(PyExc_IndexError,
1023 PyErr_SetString(PyExc_IndexError,
986 "insert only supported at index -1");
1024 "insert only supported at index -1");
987 return NULL;
1025 return NULL;
988 }
1026 }
989
1027
990 if (self->added == NULL) {
1028 if (self->added == NULL) {
991 self->added = PyList_New(0);
1029 self->added = PyList_New(0);
992 if (self->added == NULL)
1030 if (self->added == NULL)
993 return NULL;
1031 return NULL;
994 }
1032 }
995
1033
996 if (PyList_Append(self->added, obj) == -1)
1034 if (PyList_Append(self->added, obj) == -1)
997 return NULL;
1035 return NULL;
998
1036
999 if (self->nt)
1037 if (self->nt)
1000 nt_insert(self, node, index);
1038 nt_insert(self, node, index);
1001
1039
1002 Py_CLEAR(self->headrevs);
1040 Py_CLEAR(self->headrevs);
1003 Py_RETURN_NONE;
1041 Py_RETURN_NONE;
1004 }
1042 }
1005
1043
1006 static void _index_clearcaches(indexObject *self)
1044 static void _index_clearcaches(indexObject *self)
1007 {
1045 {
1008 if (self->cache) {
1046 if (self->cache) {
1009 Py_ssize_t i;
1047 Py_ssize_t i;
1010
1048
1011 for (i = 0; i < self->raw_length; i++)
1049 for (i = 0; i < self->raw_length; i++)
1012 Py_CLEAR(self->cache[i]);
1050 Py_CLEAR(self->cache[i]);
1013 free(self->cache);
1051 free(self->cache);
1014 self->cache = NULL;
1052 self->cache = NULL;
1015 }
1053 }
1016 if (self->offsets) {
1054 if (self->offsets) {
1017 free(self->offsets);
1055 free(self->offsets);
1018 self->offsets = NULL;
1056 self->offsets = NULL;
1019 }
1057 }
1020 if (self->nt) {
1058 if (self->nt) {
1021 free(self->nt);
1059 free(self->nt);
1022 self->nt = NULL;
1060 self->nt = NULL;
1023 }
1061 }
1024 Py_CLEAR(self->headrevs);
1062 Py_CLEAR(self->headrevs);
1025 }
1063 }
1026
1064
1027 static PyObject *index_clearcaches(indexObject *self)
1065 static PyObject *index_clearcaches(indexObject *self)
1028 {
1066 {
1029 _index_clearcaches(self);
1067 _index_clearcaches(self);
1030 self->ntlength = self->ntcapacity = 0;
1068 self->ntlength = self->ntcapacity = 0;
1031 self->ntdepth = self->ntsplits = 0;
1069 self->ntdepth = self->ntsplits = 0;
1032 self->ntrev = -1;
1070 self->ntrev = -1;
1033 self->ntlookups = self->ntmisses = 0;
1071 self->ntlookups = self->ntmisses = 0;
1034 Py_RETURN_NONE;
1072 Py_RETURN_NONE;
1035 }
1073 }
1036
1074
1037 static PyObject *index_stats(indexObject *self)
1075 static PyObject *index_stats(indexObject *self)
1038 {
1076 {
1039 PyObject *obj = PyDict_New();
1077 PyObject *obj = PyDict_New();
1040 PyObject *t = NULL;
1078 PyObject *t = NULL;
1041
1079
1042 if (obj == NULL)
1080 if (obj == NULL)
1043 return NULL;
1081 return NULL;
1044
1082
1045 #define istat(__n, __d) \
1083 #define istat(__n, __d) \
1046 do { \
1084 do { \
1047 t = PyInt_FromSsize_t(self->__n); \
1085 t = PyInt_FromSsize_t(self->__n); \
1048 if (!t) \
1086 if (!t) \
1049 goto bail; \
1087 goto bail; \
1050 if (PyDict_SetItemString(obj, __d, t) == -1) \
1088 if (PyDict_SetItemString(obj, __d, t) == -1) \
1051 goto bail; \
1089 goto bail; \
1052 Py_DECREF(t); \
1090 Py_DECREF(t); \
1053 } while (0)
1091 } while (0)
1054
1092
1055 if (self->added) {
1093 if (self->added) {
1056 Py_ssize_t len = PyList_GET_SIZE(self->added);
1094 Py_ssize_t len = PyList_GET_SIZE(self->added);
1057 t = PyInt_FromSsize_t(len);
1095 t = PyInt_FromSsize_t(len);
1058 if (!t)
1096 if (!t)
1059 goto bail;
1097 goto bail;
1060 if (PyDict_SetItemString(obj, "index entries added", t) == -1)
1098 if (PyDict_SetItemString(obj, "index entries added", t) == -1)
1061 goto bail;
1099 goto bail;
1062 Py_DECREF(t);
1100 Py_DECREF(t);
1063 }
1101 }
1064
1102
1065 if (self->raw_length != self->length - 1)
1103 if (self->raw_length != self->length - 1)
1066 istat(raw_length, "revs on disk");
1104 istat(raw_length, "revs on disk");
1067 istat(length, "revs in memory");
1105 istat(length, "revs in memory");
1068 istat(ntcapacity, "node trie capacity");
1106 istat(ntcapacity, "node trie capacity");
1069 istat(ntdepth, "node trie depth");
1107 istat(ntdepth, "node trie depth");
1070 istat(ntlength, "node trie count");
1108 istat(ntlength, "node trie count");
1071 istat(ntlookups, "node trie lookups");
1109 istat(ntlookups, "node trie lookups");
1072 istat(ntmisses, "node trie misses");
1110 istat(ntmisses, "node trie misses");
1073 istat(ntrev, "node trie last rev scanned");
1111 istat(ntrev, "node trie last rev scanned");
1074 istat(ntsplits, "node trie splits");
1112 istat(ntsplits, "node trie splits");
1075
1113
1076 #undef istat
1114 #undef istat
1077
1115
1078 return obj;
1116 return obj;
1079
1117
1080 bail:
1118 bail:
1081 Py_XDECREF(obj);
1119 Py_XDECREF(obj);
1082 Py_XDECREF(t);
1120 Py_XDECREF(t);
1083 return NULL;
1121 return NULL;
1084 }
1122 }
1085
1123
1086 /*
1124 /*
1087 * When we cache a list, we want to be sure the caller can't mutate
1125 * When we cache a list, we want to be sure the caller can't mutate
1088 * the cached copy.
1126 * the cached copy.
1089 */
1127 */
1090 static PyObject *list_copy(PyObject *list)
1128 static PyObject *list_copy(PyObject *list)
1091 {
1129 {
1092 Py_ssize_t len = PyList_GET_SIZE(list);
1130 Py_ssize_t len = PyList_GET_SIZE(list);
1093 PyObject *newlist = PyList_New(len);
1131 PyObject *newlist = PyList_New(len);
1094 Py_ssize_t i;
1132 Py_ssize_t i;
1095
1133
1096 if (newlist == NULL)
1134 if (newlist == NULL)
1097 return NULL;
1135 return NULL;
1098
1136
1099 for (i = 0; i < len; i++) {
1137 for (i = 0; i < len; i++) {
1100 PyObject *obj = PyList_GET_ITEM(list, i);
1138 PyObject *obj = PyList_GET_ITEM(list, i);
1101 Py_INCREF(obj);
1139 Py_INCREF(obj);
1102 PyList_SET_ITEM(newlist, i, obj);
1140 PyList_SET_ITEM(newlist, i, obj);
1103 }
1141 }
1104
1142
1105 return newlist;
1143 return newlist;
1106 }
1144 }
1107
1145
1108 static int check_filter(PyObject *filter, Py_ssize_t arg) {
1146 static int check_filter(PyObject *filter, Py_ssize_t arg) {
1109 if (filter) {
1147 if (filter) {
1110 PyObject *arglist, *result;
1148 PyObject *arglist, *result;
1111 int isfiltered;
1149 int isfiltered;
1112
1150
1113 arglist = Py_BuildValue("(n)", arg);
1151 arglist = Py_BuildValue("(n)", arg);
1114 if (!arglist) {
1152 if (!arglist) {
1115 return -1;
1153 return -1;
1116 }
1154 }
1117
1155
1118 result = PyEval_CallObject(filter, arglist);
1156 result = PyEval_CallObject(filter, arglist);
1119 Py_DECREF(arglist);
1157 Py_DECREF(arglist);
1120 if (!result) {
1158 if (!result) {
1121 return -1;
1159 return -1;
1122 }
1160 }
1123
1161
1124 /* PyObject_IsTrue returns 1 if true, 0 if false, -1 if error,
1162 /* PyObject_IsTrue returns 1 if true, 0 if false, -1 if error,
1125 * same as this function, so we can just return it directly.*/
1163 * same as this function, so we can just return it directly.*/
1126 isfiltered = PyObject_IsTrue(result);
1164 isfiltered = PyObject_IsTrue(result);
1127 Py_DECREF(result);
1165 Py_DECREF(result);
1128 return isfiltered;
1166 return isfiltered;
1129 } else {
1167 } else {
1130 return 0;
1168 return 0;
1131 }
1169 }
1132 }
1170 }
1133
1171
1134 static Py_ssize_t add_roots_get_min(indexObject *self, PyObject *list,
1172 static Py_ssize_t add_roots_get_min(indexObject *self, PyObject *list,
1135 Py_ssize_t marker, char *phases)
1173 Py_ssize_t marker, char *phases)
1136 {
1174 {
1137 PyObject *iter = NULL;
1175 PyObject *iter = NULL;
1138 PyObject *iter_item = NULL;
1176 PyObject *iter_item = NULL;
1139 Py_ssize_t min_idx = index_length(self) + 1;
1177 Py_ssize_t min_idx = index_length(self) + 1;
1140 long iter_item_long;
1178 long iter_item_long;
1141
1179
1142 if (PyList_GET_SIZE(list) != 0) {
1180 if (PyList_GET_SIZE(list) != 0) {
1143 iter = PyObject_GetIter(list);
1181 iter = PyObject_GetIter(list);
1144 if (iter == NULL)
1182 if (iter == NULL)
1145 return -2;
1183 return -2;
1146 while ((iter_item = PyIter_Next(iter)))
1184 while ((iter_item = PyIter_Next(iter)))
1147 {
1185 {
1148 iter_item_long = PyInt_AS_LONG(iter_item);
1186 iter_item_long = PyInt_AS_LONG(iter_item);
1149 Py_DECREF(iter_item);
1187 Py_DECREF(iter_item);
1150 if (iter_item_long < min_idx)
1188 if (iter_item_long < min_idx)
1151 min_idx = iter_item_long;
1189 min_idx = iter_item_long;
1152 phases[iter_item_long] = marker;
1190 phases[iter_item_long] = marker;
1153 }
1191 }
1154 Py_DECREF(iter);
1192 Py_DECREF(iter);
1155 }
1193 }
1156
1194
1157 return min_idx;
1195 return min_idx;
1158 }
1196 }
1159
1197
1160 static inline void set_phase_from_parents(char *phases, int parent_1,
1198 static inline void set_phase_from_parents(char *phases, int parent_1,
1161 int parent_2, Py_ssize_t i)
1199 int parent_2, Py_ssize_t i)
1162 {
1200 {
1163 if (parent_1 >= 0 && phases[parent_1] > phases[i])
1201 if (parent_1 >= 0 && phases[parent_1] > phases[i])
1164 phases[i] = phases[parent_1];
1202 phases[i] = phases[parent_1];
1165 if (parent_2 >= 0 && phases[parent_2] > phases[i])
1203 if (parent_2 >= 0 && phases[parent_2] > phases[i])
1166 phases[i] = phases[parent_2];
1204 phases[i] = phases[parent_2];
1167 }
1205 }
1168
1206
1169 static PyObject *reachableroots2(indexObject *self, PyObject *args)
1207 static PyObject *reachableroots2(indexObject *self, PyObject *args)
1170 {
1208 {
1171
1209
1172 /* Input */
1210 /* Input */
1173 long minroot;
1211 long minroot;
1174 PyObject *includepatharg = NULL;
1212 PyObject *includepatharg = NULL;
1175 int includepath = 0;
1213 int includepath = 0;
1176 /* heads and roots are lists */
1214 /* heads and roots are lists */
1177 PyObject *heads = NULL;
1215 PyObject *heads = NULL;
1178 PyObject *roots = NULL;
1216 PyObject *roots = NULL;
1179 PyObject *reachable = NULL;
1217 PyObject *reachable = NULL;
1180
1218
1181 PyObject *val;
1219 PyObject *val;
1182 Py_ssize_t len = index_length(self) - 1;
1220 Py_ssize_t len = index_length(self) - 1;
1183 long revnum;
1221 long revnum;
1184 Py_ssize_t k;
1222 Py_ssize_t k;
1185 Py_ssize_t i;
1223 Py_ssize_t i;
1186 Py_ssize_t l;
1224 Py_ssize_t l;
1187 int r;
1225 int r;
1188 int parents[2];
1226 int parents[2];
1189
1227
1190 /* Internal data structure:
1228 /* Internal data structure:
1191 * tovisit: array of length len+1 (all revs + nullrev), filled upto lentovisit
1229 * tovisit: array of length len+1 (all revs + nullrev), filled upto lentovisit
1192 * revstates: array of length len+1 (all revs + nullrev) */
1230 * revstates: array of length len+1 (all revs + nullrev) */
1193 int *tovisit = NULL;
1231 int *tovisit = NULL;
1194 long lentovisit = 0;
1232 long lentovisit = 0;
1195 enum { RS_SEEN = 1, RS_ROOT = 2, RS_REACHABLE = 4 };
1233 enum { RS_SEEN = 1, RS_ROOT = 2, RS_REACHABLE = 4 };
1196 char *revstates = NULL;
1234 char *revstates = NULL;
1197
1235
1198 /* Get arguments */
1236 /* Get arguments */
1199 if (!PyArg_ParseTuple(args, "lO!O!O!", &minroot, &PyList_Type, &heads,
1237 if (!PyArg_ParseTuple(args, "lO!O!O!", &minroot, &PyList_Type, &heads,
1200 &PyList_Type, &roots,
1238 &PyList_Type, &roots,
1201 &PyBool_Type, &includepatharg))
1239 &PyBool_Type, &includepatharg))
1202 goto bail;
1240 goto bail;
1203
1241
1204 if (includepatharg == Py_True)
1242 if (includepatharg == Py_True)
1205 includepath = 1;
1243 includepath = 1;
1206
1244
1207 /* Initialize return set */
1245 /* Initialize return set */
1208 reachable = PyList_New(0);
1246 reachable = PyList_New(0);
1209 if (reachable == NULL)
1247 if (reachable == NULL)
1210 goto bail;
1248 goto bail;
1211
1249
1212 /* Initialize internal datastructures */
1250 /* Initialize internal datastructures */
1213 tovisit = (int *)malloc((len + 1) * sizeof(int));
1251 tovisit = (int *)malloc((len + 1) * sizeof(int));
1214 if (tovisit == NULL) {
1252 if (tovisit == NULL) {
1215 PyErr_NoMemory();
1253 PyErr_NoMemory();
1216 goto bail;
1254 goto bail;
1217 }
1255 }
1218
1256
1219 revstates = (char *)calloc(len + 1, 1);
1257 revstates = (char *)calloc(len + 1, 1);
1220 if (revstates == NULL) {
1258 if (revstates == NULL) {
1221 PyErr_NoMemory();
1259 PyErr_NoMemory();
1222 goto bail;
1260 goto bail;
1223 }
1261 }
1224
1262
1225 l = PyList_GET_SIZE(roots);
1263 l = PyList_GET_SIZE(roots);
1226 for (i = 0; i < l; i++) {
1264 for (i = 0; i < l; i++) {
1227 revnum = PyInt_AsLong(PyList_GET_ITEM(roots, i));
1265 revnum = PyInt_AsLong(PyList_GET_ITEM(roots, i));
1228 if (revnum == -1 && PyErr_Occurred())
1266 if (revnum == -1 && PyErr_Occurred())
1229 goto bail;
1267 goto bail;
1230 /* If root is out of range, e.g. wdir(), it must be unreachable
1268 /* If root is out of range, e.g. wdir(), it must be unreachable
1231 * from heads. So we can just ignore it. */
1269 * from heads. So we can just ignore it. */
1232 if (revnum + 1 < 0 || revnum + 1 >= len + 1)
1270 if (revnum + 1 < 0 || revnum + 1 >= len + 1)
1233 continue;
1271 continue;
1234 revstates[revnum + 1] |= RS_ROOT;
1272 revstates[revnum + 1] |= RS_ROOT;
1235 }
1273 }
1236
1274
1237 /* Populate tovisit with all the heads */
1275 /* Populate tovisit with all the heads */
1238 l = PyList_GET_SIZE(heads);
1276 l = PyList_GET_SIZE(heads);
1239 for (i = 0; i < l; i++) {
1277 for (i = 0; i < l; i++) {
1240 revnum = PyInt_AsLong(PyList_GET_ITEM(heads, i));
1278 revnum = PyInt_AsLong(PyList_GET_ITEM(heads, i));
1241 if (revnum == -1 && PyErr_Occurred())
1279 if (revnum == -1 && PyErr_Occurred())
1242 goto bail;
1280 goto bail;
1243 if (revnum + 1 < 0 || revnum + 1 >= len + 1) {
1281 if (revnum + 1 < 0 || revnum + 1 >= len + 1) {
1244 PyErr_SetString(PyExc_IndexError, "head out of range");
1282 PyErr_SetString(PyExc_IndexError, "head out of range");
1245 goto bail;
1283 goto bail;
1246 }
1284 }
1247 if (!(revstates[revnum + 1] & RS_SEEN)) {
1285 if (!(revstates[revnum + 1] & RS_SEEN)) {
1248 tovisit[lentovisit++] = (int)revnum;
1286 tovisit[lentovisit++] = (int)revnum;
1249 revstates[revnum + 1] |= RS_SEEN;
1287 revstates[revnum + 1] |= RS_SEEN;
1250 }
1288 }
1251 }
1289 }
1252
1290
1253 /* Visit the tovisit list and find the reachable roots */
1291 /* Visit the tovisit list and find the reachable roots */
1254 k = 0;
1292 k = 0;
1255 while (k < lentovisit) {
1293 while (k < lentovisit) {
1256 /* Add the node to reachable if it is a root*/
1294 /* Add the node to reachable if it is a root*/
1257 revnum = tovisit[k++];
1295 revnum = tovisit[k++];
1258 if (revstates[revnum + 1] & RS_ROOT) {
1296 if (revstates[revnum + 1] & RS_ROOT) {
1259 revstates[revnum + 1] |= RS_REACHABLE;
1297 revstates[revnum + 1] |= RS_REACHABLE;
1260 val = PyInt_FromLong(revnum);
1298 val = PyInt_FromLong(revnum);
1261 if (val == NULL)
1299 if (val == NULL)
1262 goto bail;
1300 goto bail;
1263 r = PyList_Append(reachable, val);
1301 r = PyList_Append(reachable, val);
1264 Py_DECREF(val);
1302 Py_DECREF(val);
1265 if (r < 0)
1303 if (r < 0)
1266 goto bail;
1304 goto bail;
1267 if (includepath == 0)
1305 if (includepath == 0)
1268 continue;
1306 continue;
1269 }
1307 }
1270
1308
1271 /* Add its parents to the list of nodes to visit */
1309 /* Add its parents to the list of nodes to visit */
1272 if (revnum == -1)
1310 if (revnum == -1)
1273 continue;
1311 continue;
1274 r = index_get_parents(self, revnum, parents, (int)len - 1);
1312 r = index_get_parents(self, revnum, parents, (int)len - 1);
1275 if (r < 0)
1313 if (r < 0)
1276 goto bail;
1314 goto bail;
1277 for (i = 0; i < 2; i++) {
1315 for (i = 0; i < 2; i++) {
1278 if (!(revstates[parents[i] + 1] & RS_SEEN)
1316 if (!(revstates[parents[i] + 1] & RS_SEEN)
1279 && parents[i] >= minroot) {
1317 && parents[i] >= minroot) {
1280 tovisit[lentovisit++] = parents[i];
1318 tovisit[lentovisit++] = parents[i];
1281 revstates[parents[i] + 1] |= RS_SEEN;
1319 revstates[parents[i] + 1] |= RS_SEEN;
1282 }
1320 }
1283 }
1321 }
1284 }
1322 }
1285
1323
1286 /* Find all the nodes in between the roots we found and the heads
1324 /* Find all the nodes in between the roots we found and the heads
1287 * and add them to the reachable set */
1325 * and add them to the reachable set */
1288 if (includepath == 1) {
1326 if (includepath == 1) {
1289 long minidx = minroot;
1327 long minidx = minroot;
1290 if (minidx < 0)
1328 if (minidx < 0)
1291 minidx = 0;
1329 minidx = 0;
1292 for (i = minidx; i < len; i++) {
1330 for (i = minidx; i < len; i++) {
1293 if (!(revstates[i + 1] & RS_SEEN))
1331 if (!(revstates[i + 1] & RS_SEEN))
1294 continue;
1332 continue;
1295 r = index_get_parents(self, i, parents, (int)len - 1);
1333 r = index_get_parents(self, i, parents, (int)len - 1);
1296 /* Corrupted index file, error is set from
1334 /* Corrupted index file, error is set from
1297 * index_get_parents */
1335 * index_get_parents */
1298 if (r < 0)
1336 if (r < 0)
1299 goto bail;
1337 goto bail;
1300 if (((revstates[parents[0] + 1] |
1338 if (((revstates[parents[0] + 1] |
1301 revstates[parents[1] + 1]) & RS_REACHABLE)
1339 revstates[parents[1] + 1]) & RS_REACHABLE)
1302 && !(revstates[i + 1] & RS_REACHABLE)) {
1340 && !(revstates[i + 1] & RS_REACHABLE)) {
1303 revstates[i + 1] |= RS_REACHABLE;
1341 revstates[i + 1] |= RS_REACHABLE;
1304 val = PyInt_FromLong(i);
1342 val = PyInt_FromLong(i);
1305 if (val == NULL)
1343 if (val == NULL)
1306 goto bail;
1344 goto bail;
1307 r = PyList_Append(reachable, val);
1345 r = PyList_Append(reachable, val);
1308 Py_DECREF(val);
1346 Py_DECREF(val);
1309 if (r < 0)
1347 if (r < 0)
1310 goto bail;
1348 goto bail;
1311 }
1349 }
1312 }
1350 }
1313 }
1351 }
1314
1352
1315 free(revstates);
1353 free(revstates);
1316 free(tovisit);
1354 free(tovisit);
1317 return reachable;
1355 return reachable;
1318 bail:
1356 bail:
1319 Py_XDECREF(reachable);
1357 Py_XDECREF(reachable);
1320 free(revstates);
1358 free(revstates);
1321 free(tovisit);
1359 free(tovisit);
1322 return NULL;
1360 return NULL;
1323 }
1361 }
1324
1362
1325 static PyObject *compute_phases_map_sets(indexObject *self, PyObject *args)
1363 static PyObject *compute_phases_map_sets(indexObject *self, PyObject *args)
1326 {
1364 {
1327 PyObject *roots = Py_None;
1365 PyObject *roots = Py_None;
1328 PyObject *ret = NULL;
1366 PyObject *ret = NULL;
1329 PyObject *phaseslist = NULL;
1367 PyObject *phaseslist = NULL;
1330 PyObject *phaseroots = NULL;
1368 PyObject *phaseroots = NULL;
1331 PyObject *phaseset = NULL;
1369 PyObject *phaseset = NULL;
1332 PyObject *phasessetlist = NULL;
1370 PyObject *phasessetlist = NULL;
1333 PyObject *rev = NULL;
1371 PyObject *rev = NULL;
1334 Py_ssize_t len = index_length(self) - 1;
1372 Py_ssize_t len = index_length(self) - 1;
1335 Py_ssize_t numphase = 0;
1373 Py_ssize_t numphase = 0;
1336 Py_ssize_t minrevallphases = 0;
1374 Py_ssize_t minrevallphases = 0;
1337 Py_ssize_t minrevphase = 0;
1375 Py_ssize_t minrevphase = 0;
1338 Py_ssize_t i = 0;
1376 Py_ssize_t i = 0;
1339 char *phases = NULL;
1377 char *phases = NULL;
1340 long phase;
1378 long phase;
1341
1379
1342 if (!PyArg_ParseTuple(args, "O", &roots))
1380 if (!PyArg_ParseTuple(args, "O", &roots))
1343 goto done;
1381 goto done;
1344 if (roots == NULL || !PyList_Check(roots))
1382 if (roots == NULL || !PyList_Check(roots))
1345 goto done;
1383 goto done;
1346
1384
1347 phases = calloc(len, 1); /* phase per rev: {0: public, 1: draft, 2: secret} */
1385 phases = calloc(len, 1); /* phase per rev: {0: public, 1: draft, 2: secret} */
1348 if (phases == NULL) {
1386 if (phases == NULL) {
1349 PyErr_NoMemory();
1387 PyErr_NoMemory();
1350 goto done;
1388 goto done;
1351 }
1389 }
1352 /* Put the phase information of all the roots in phases */
1390 /* Put the phase information of all the roots in phases */
1353 numphase = PyList_GET_SIZE(roots)+1;
1391 numphase = PyList_GET_SIZE(roots)+1;
1354 minrevallphases = len + 1;
1392 minrevallphases = len + 1;
1355 phasessetlist = PyList_New(numphase);
1393 phasessetlist = PyList_New(numphase);
1356 if (phasessetlist == NULL)
1394 if (phasessetlist == NULL)
1357 goto done;
1395 goto done;
1358
1396
1359 PyList_SET_ITEM(phasessetlist, 0, Py_None);
1397 PyList_SET_ITEM(phasessetlist, 0, Py_None);
1360 Py_INCREF(Py_None);
1398 Py_INCREF(Py_None);
1361
1399
1362 for (i = 0; i < numphase-1; i++) {
1400 for (i = 0; i < numphase-1; i++) {
1363 phaseroots = PyList_GET_ITEM(roots, i);
1401 phaseroots = PyList_GET_ITEM(roots, i);
1364 phaseset = PySet_New(NULL);
1402 phaseset = PySet_New(NULL);
1365 if (phaseset == NULL)
1403 if (phaseset == NULL)
1366 goto release;
1404 goto release;
1367 PyList_SET_ITEM(phasessetlist, i+1, phaseset);
1405 PyList_SET_ITEM(phasessetlist, i+1, phaseset);
1368 if (!PyList_Check(phaseroots))
1406 if (!PyList_Check(phaseroots))
1369 goto release;
1407 goto release;
1370 minrevphase = add_roots_get_min(self, phaseroots, i+1, phases);
1408 minrevphase = add_roots_get_min(self, phaseroots, i+1, phases);
1371 if (minrevphase == -2) /* Error from add_roots_get_min */
1409 if (minrevphase == -2) /* Error from add_roots_get_min */
1372 goto release;
1410 goto release;
1373 minrevallphases = MIN(minrevallphases, minrevphase);
1411 minrevallphases = MIN(minrevallphases, minrevphase);
1374 }
1412 }
1375 /* Propagate the phase information from the roots to the revs */
1413 /* Propagate the phase information from the roots to the revs */
1376 if (minrevallphases != -1) {
1414 if (minrevallphases != -1) {
1377 int parents[2];
1415 int parents[2];
1378 for (i = minrevallphases; i < len; i++) {
1416 for (i = minrevallphases; i < len; i++) {
1379 if (index_get_parents(self, i, parents,
1417 if (index_get_parents(self, i, parents,
1380 (int)len - 1) < 0)
1418 (int)len - 1) < 0)
1381 goto release;
1419 goto release;
1382 set_phase_from_parents(phases, parents[0], parents[1], i);
1420 set_phase_from_parents(phases, parents[0], parents[1], i);
1383 }
1421 }
1384 }
1422 }
1385 /* Transform phase list to a python list */
1423 /* Transform phase list to a python list */
1386 phaseslist = PyList_New(len);
1424 phaseslist = PyList_New(len);
1387 if (phaseslist == NULL)
1425 if (phaseslist == NULL)
1388 goto release;
1426 goto release;
1389 for (i = 0; i < len; i++) {
1427 for (i = 0; i < len; i++) {
1390 PyObject *phaseval;
1428 PyObject *phaseval;
1391
1429
1392 phase = phases[i];
1430 phase = phases[i];
1393 /* We only store the sets of phase for non public phase, the public phase
1431 /* We only store the sets of phase for non public phase, the public phase
1394 * is computed as a difference */
1432 * is computed as a difference */
1395 if (phase != 0) {
1433 if (phase != 0) {
1396 phaseset = PyList_GET_ITEM(phasessetlist, phase);
1434 phaseset = PyList_GET_ITEM(phasessetlist, phase);
1397 rev = PyInt_FromLong(i);
1435 rev = PyInt_FromLong(i);
1398 if (rev == NULL)
1436 if (rev == NULL)
1399 goto release;
1437 goto release;
1400 PySet_Add(phaseset, rev);
1438 PySet_Add(phaseset, rev);
1401 Py_XDECREF(rev);
1439 Py_XDECREF(rev);
1402 }
1440 }
1403 phaseval = PyInt_FromLong(phase);
1441 phaseval = PyInt_FromLong(phase);
1404 if (phaseval == NULL)
1442 if (phaseval == NULL)
1405 goto release;
1443 goto release;
1406 PyList_SET_ITEM(phaseslist, i, phaseval);
1444 PyList_SET_ITEM(phaseslist, i, phaseval);
1407 }
1445 }
1408 ret = PyTuple_Pack(2, phaseslist, phasessetlist);
1446 ret = PyTuple_Pack(2, phaseslist, phasessetlist);
1409
1447
1410 release:
1448 release:
1411 Py_XDECREF(phaseslist);
1449 Py_XDECREF(phaseslist);
1412 Py_XDECREF(phasessetlist);
1450 Py_XDECREF(phasessetlist);
1413 done:
1451 done:
1414 free(phases);
1452 free(phases);
1415 return ret;
1453 return ret;
1416 }
1454 }
1417
1455
1418 static PyObject *index_headrevs(indexObject *self, PyObject *args)
1456 static PyObject *index_headrevs(indexObject *self, PyObject *args)
1419 {
1457 {
1420 Py_ssize_t i, j, len;
1458 Py_ssize_t i, j, len;
1421 char *nothead = NULL;
1459 char *nothead = NULL;
1422 PyObject *heads = NULL;
1460 PyObject *heads = NULL;
1423 PyObject *filter = NULL;
1461 PyObject *filter = NULL;
1424 PyObject *filteredrevs = Py_None;
1462 PyObject *filteredrevs = Py_None;
1425
1463
1426 if (!PyArg_ParseTuple(args, "|O", &filteredrevs)) {
1464 if (!PyArg_ParseTuple(args, "|O", &filteredrevs)) {
1427 return NULL;
1465 return NULL;
1428 }
1466 }
1429
1467
1430 if (self->headrevs && filteredrevs == self->filteredrevs)
1468 if (self->headrevs && filteredrevs == self->filteredrevs)
1431 return list_copy(self->headrevs);
1469 return list_copy(self->headrevs);
1432
1470
1433 Py_DECREF(self->filteredrevs);
1471 Py_DECREF(self->filteredrevs);
1434 self->filteredrevs = filteredrevs;
1472 self->filteredrevs = filteredrevs;
1435 Py_INCREF(filteredrevs);
1473 Py_INCREF(filteredrevs);
1436
1474
1437 if (filteredrevs != Py_None) {
1475 if (filteredrevs != Py_None) {
1438 filter = PyObject_GetAttrString(filteredrevs, "__contains__");
1476 filter = PyObject_GetAttrString(filteredrevs, "__contains__");
1439 if (!filter) {
1477 if (!filter) {
1440 PyErr_SetString(PyExc_TypeError,
1478 PyErr_SetString(PyExc_TypeError,
1441 "filteredrevs has no attribute __contains__");
1479 "filteredrevs has no attribute __contains__");
1442 goto bail;
1480 goto bail;
1443 }
1481 }
1444 }
1482 }
1445
1483
1446 len = index_length(self) - 1;
1484 len = index_length(self) - 1;
1447 heads = PyList_New(0);
1485 heads = PyList_New(0);
1448 if (heads == NULL)
1486 if (heads == NULL)
1449 goto bail;
1487 goto bail;
1450 if (len == 0) {
1488 if (len == 0) {
1451 PyObject *nullid = PyInt_FromLong(-1);
1489 PyObject *nullid = PyInt_FromLong(-1);
1452 if (nullid == NULL || PyList_Append(heads, nullid) == -1) {
1490 if (nullid == NULL || PyList_Append(heads, nullid) == -1) {
1453 Py_XDECREF(nullid);
1491 Py_XDECREF(nullid);
1454 goto bail;
1492 goto bail;
1455 }
1493 }
1456 goto done;
1494 goto done;
1457 }
1495 }
1458
1496
1459 nothead = calloc(len, 1);
1497 nothead = calloc(len, 1);
1460 if (nothead == NULL) {
1498 if (nothead == NULL) {
1461 PyErr_NoMemory();
1499 PyErr_NoMemory();
1462 goto bail;
1500 goto bail;
1463 }
1501 }
1464
1502
1465 for (i = len - 1; i >= 0; i--) {
1503 for (i = len - 1; i >= 0; i--) {
1466 int isfiltered;
1504 int isfiltered;
1467 int parents[2];
1505 int parents[2];
1468
1506
1469 /* If nothead[i] == 1, it means we've seen an unfiltered child of this
1507 /* If nothead[i] == 1, it means we've seen an unfiltered child of this
1470 * node already, and therefore this node is not filtered. So we can skip
1508 * node already, and therefore this node is not filtered. So we can skip
1471 * the expensive check_filter step.
1509 * the expensive check_filter step.
1472 */
1510 */
1473 if (nothead[i] != 1) {
1511 if (nothead[i] != 1) {
1474 isfiltered = check_filter(filter, i);
1512 isfiltered = check_filter(filter, i);
1475 if (isfiltered == -1) {
1513 if (isfiltered == -1) {
1476 PyErr_SetString(PyExc_TypeError,
1514 PyErr_SetString(PyExc_TypeError,
1477 "unable to check filter");
1515 "unable to check filter");
1478 goto bail;
1516 goto bail;
1479 }
1517 }
1480
1518
1481 if (isfiltered) {
1519 if (isfiltered) {
1482 nothead[i] = 1;
1520 nothead[i] = 1;
1483 continue;
1521 continue;
1484 }
1522 }
1485 }
1523 }
1486
1524
1487 if (index_get_parents(self, i, parents, (int)len - 1) < 0)
1525 if (index_get_parents(self, i, parents, (int)len - 1) < 0)
1488 goto bail;
1526 goto bail;
1489 for (j = 0; j < 2; j++) {
1527 for (j = 0; j < 2; j++) {
1490 if (parents[j] >= 0)
1528 if (parents[j] >= 0)
1491 nothead[parents[j]] = 1;
1529 nothead[parents[j]] = 1;
1492 }
1530 }
1493 }
1531 }
1494
1532
1495 for (i = 0; i < len; i++) {
1533 for (i = 0; i < len; i++) {
1496 PyObject *head;
1534 PyObject *head;
1497
1535
1498 if (nothead[i])
1536 if (nothead[i])
1499 continue;
1537 continue;
1500 head = PyInt_FromSsize_t(i);
1538 head = PyInt_FromSsize_t(i);
1501 if (head == NULL || PyList_Append(heads, head) == -1) {
1539 if (head == NULL || PyList_Append(heads, head) == -1) {
1502 Py_XDECREF(head);
1540 Py_XDECREF(head);
1503 goto bail;
1541 goto bail;
1504 }
1542 }
1505 }
1543 }
1506
1544
1507 done:
1545 done:
1508 self->headrevs = heads;
1546 self->headrevs = heads;
1509 Py_XDECREF(filter);
1547 Py_XDECREF(filter);
1510 free(nothead);
1548 free(nothead);
1511 return list_copy(self->headrevs);
1549 return list_copy(self->headrevs);
1512 bail:
1550 bail:
1513 Py_XDECREF(filter);
1551 Py_XDECREF(filter);
1514 Py_XDECREF(heads);
1552 Py_XDECREF(heads);
1515 free(nothead);
1553 free(nothead);
1516 return NULL;
1554 return NULL;
1517 }
1555 }
1518
1556
1519 static inline int nt_level(const char *node, Py_ssize_t level)
1557 static inline int nt_level(const char *node, Py_ssize_t level)
1520 {
1558 {
1521 int v = node[level>>1];
1559 int v = node[level>>1];
1522 if (!(level & 1))
1560 if (!(level & 1))
1523 v >>= 4;
1561 v >>= 4;
1524 return v & 0xf;
1562 return v & 0xf;
1525 }
1563 }
1526
1564
1527 /*
1565 /*
1528 * Return values:
1566 * Return values:
1529 *
1567 *
1530 * -4: match is ambiguous (multiple candidates)
1568 * -4: match is ambiguous (multiple candidates)
1531 * -2: not found
1569 * -2: not found
1532 * rest: valid rev
1570 * rest: valid rev
1533 */
1571 */
1534 static int nt_find(indexObject *self, const char *node, Py_ssize_t nodelen,
1572 static int nt_find(indexObject *self, const char *node, Py_ssize_t nodelen,
1535 int hex)
1573 int hex)
1536 {
1574 {
1537 int (*getnybble)(const char *, Py_ssize_t) = hex ? hexdigit : nt_level;
1575 int (*getnybble)(const char *, Py_ssize_t) = hex ? hexdigit : nt_level;
1538 int level, maxlevel, off;
1576 int level, maxlevel, off;
1539
1577
1540 if (nodelen == 20 && node[0] == '\0' && memcmp(node, nullid, 20) == 0)
1578 if (nodelen == 20 && node[0] == '\0' && memcmp(node, nullid, 20) == 0)
1541 return -1;
1579 return -1;
1542
1580
1543 if (self->nt == NULL)
1581 if (self->nt == NULL)
1544 return -2;
1582 return -2;
1545
1583
1546 if (hex)
1584 if (hex)
1547 maxlevel = nodelen > 40 ? 40 : (int)nodelen;
1585 maxlevel = nodelen > 40 ? 40 : (int)nodelen;
1548 else
1586 else
1549 maxlevel = nodelen > 20 ? 40 : ((int)nodelen * 2);
1587 maxlevel = nodelen > 20 ? 40 : ((int)nodelen * 2);
1550
1588
1551 for (level = off = 0; level < maxlevel; level++) {
1589 for (level = off = 0; level < maxlevel; level++) {
1552 int k = getnybble(node, level);
1590 int k = getnybble(node, level);
1553 nodetree *n = &self->nt[off];
1591 nodetree *n = &self->nt[off];
1554 int v = n->children[k];
1592 int v = n->children[k];
1555
1593
1556 if (v < 0) {
1594 if (v < 0) {
1557 const char *n;
1595 const char *n;
1558 Py_ssize_t i;
1596 Py_ssize_t i;
1559
1597
1560 v = -(v + 1);
1598 v = -(v + 1);
1561 n = index_node(self, v);
1599 n = index_node(self, v);
1562 if (n == NULL)
1600 if (n == NULL)
1563 return -2;
1601 return -2;
1564 for (i = level; i < maxlevel; i++)
1602 for (i = level; i < maxlevel; i++)
1565 if (getnybble(node, i) != nt_level(n, i))
1603 if (getnybble(node, i) != nt_level(n, i))
1566 return -2;
1604 return -2;
1567 return v;
1605 return v;
1568 }
1606 }
1569 if (v == 0)
1607 if (v == 0)
1570 return -2;
1608 return -2;
1571 off = v;
1609 off = v;
1572 }
1610 }
1573 /* multiple matches against an ambiguous prefix */
1611 /* multiple matches against an ambiguous prefix */
1574 return -4;
1612 return -4;
1575 }
1613 }
1576
1614
1577 static int nt_new(indexObject *self)
1615 static int nt_new(indexObject *self)
1578 {
1616 {
1579 if (self->ntlength == self->ntcapacity) {
1617 if (self->ntlength == self->ntcapacity) {
1580 if (self->ntcapacity >= INT_MAX / (sizeof(nodetree) * 2)) {
1618 if (self->ntcapacity >= INT_MAX / (sizeof(nodetree) * 2)) {
1581 PyErr_SetString(PyExc_MemoryError,
1619 PyErr_SetString(PyExc_MemoryError,
1582 "overflow in nt_new");
1620 "overflow in nt_new");
1583 return -1;
1621 return -1;
1584 }
1622 }
1585 self->ntcapacity *= 2;
1623 self->ntcapacity *= 2;
1586 self->nt = realloc(self->nt,
1624 self->nt = realloc(self->nt,
1587 self->ntcapacity * sizeof(nodetree));
1625 self->ntcapacity * sizeof(nodetree));
1588 if (self->nt == NULL) {
1626 if (self->nt == NULL) {
1589 PyErr_SetString(PyExc_MemoryError, "out of memory");
1627 PyErr_SetString(PyExc_MemoryError, "out of memory");
1590 return -1;
1628 return -1;
1591 }
1629 }
1592 memset(&self->nt[self->ntlength], 0,
1630 memset(&self->nt[self->ntlength], 0,
1593 sizeof(nodetree) * (self->ntcapacity - self->ntlength));
1631 sizeof(nodetree) * (self->ntcapacity - self->ntlength));
1594 }
1632 }
1595 return self->ntlength++;
1633 return self->ntlength++;
1596 }
1634 }
1597
1635
1598 static int nt_insert(indexObject *self, const char *node, int rev)
1636 static int nt_insert(indexObject *self, const char *node, int rev)
1599 {
1637 {
1600 int level = 0;
1638 int level = 0;
1601 int off = 0;
1639 int off = 0;
1602
1640
1603 while (level < 40) {
1641 while (level < 40) {
1604 int k = nt_level(node, level);
1642 int k = nt_level(node, level);
1605 nodetree *n;
1643 nodetree *n;
1606 int v;
1644 int v;
1607
1645
1608 n = &self->nt[off];
1646 n = &self->nt[off];
1609 v = n->children[k];
1647 v = n->children[k];
1610
1648
1611 if (v == 0) {
1649 if (v == 0) {
1612 n->children[k] = -rev - 1;
1650 n->children[k] = -rev - 1;
1613 return 0;
1651 return 0;
1614 }
1652 }
1615 if (v < 0) {
1653 if (v < 0) {
1616 const char *oldnode = index_node(self, -(v + 1));
1654 const char *oldnode = index_node(self, -(v + 1));
1617 int noff;
1655 int noff;
1618
1656
1619 if (!oldnode || !memcmp(oldnode, node, 20)) {
1657 if (!oldnode || !memcmp(oldnode, node, 20)) {
1620 n->children[k] = -rev - 1;
1658 n->children[k] = -rev - 1;
1621 return 0;
1659 return 0;
1622 }
1660 }
1623 noff = nt_new(self);
1661 noff = nt_new(self);
1624 if (noff == -1)
1662 if (noff == -1)
1625 return -1;
1663 return -1;
1626 /* self->nt may have been changed by realloc */
1664 /* self->nt may have been changed by realloc */
1627 self->nt[off].children[k] = noff;
1665 self->nt[off].children[k] = noff;
1628 off = noff;
1666 off = noff;
1629 n = &self->nt[off];
1667 n = &self->nt[off];
1630 n->children[nt_level(oldnode, ++level)] = v;
1668 n->children[nt_level(oldnode, ++level)] = v;
1631 if (level > self->ntdepth)
1669 if (level > self->ntdepth)
1632 self->ntdepth = level;
1670 self->ntdepth = level;
1633 self->ntsplits += 1;
1671 self->ntsplits += 1;
1634 } else {
1672 } else {
1635 level += 1;
1673 level += 1;
1636 off = v;
1674 off = v;
1637 }
1675 }
1638 }
1676 }
1639
1677
1640 return -1;
1678 return -1;
1641 }
1679 }
1642
1680
1643 static int nt_init(indexObject *self)
1681 static int nt_init(indexObject *self)
1644 {
1682 {
1645 if (self->nt == NULL) {
1683 if (self->nt == NULL) {
1646 if ((size_t)self->raw_length > INT_MAX / sizeof(nodetree)) {
1684 if ((size_t)self->raw_length > INT_MAX / sizeof(nodetree)) {
1647 PyErr_SetString(PyExc_ValueError, "overflow in nt_init");
1685 PyErr_SetString(PyExc_ValueError, "overflow in nt_init");
1648 return -1;
1686 return -1;
1649 }
1687 }
1650 self->ntcapacity = self->raw_length < 4
1688 self->ntcapacity = self->raw_length < 4
1651 ? 4 : (int)self->raw_length / 2;
1689 ? 4 : (int)self->raw_length / 2;
1652
1690
1653 self->nt = calloc(self->ntcapacity, sizeof(nodetree));
1691 self->nt = calloc(self->ntcapacity, sizeof(nodetree));
1654 if (self->nt == NULL) {
1692 if (self->nt == NULL) {
1655 PyErr_NoMemory();
1693 PyErr_NoMemory();
1656 return -1;
1694 return -1;
1657 }
1695 }
1658 self->ntlength = 1;
1696 self->ntlength = 1;
1659 self->ntrev = (int)index_length(self) - 1;
1697 self->ntrev = (int)index_length(self) - 1;
1660 self->ntlookups = 1;
1698 self->ntlookups = 1;
1661 self->ntmisses = 0;
1699 self->ntmisses = 0;
1662 if (nt_insert(self, nullid, INT_MAX) == -1)
1700 if (nt_insert(self, nullid, INT_MAX) == -1)
1663 return -1;
1701 return -1;
1664 }
1702 }
1665 return 0;
1703 return 0;
1666 }
1704 }
1667
1705
1668 /*
1706 /*
1669 * Return values:
1707 * Return values:
1670 *
1708 *
1671 * -3: error (exception set)
1709 * -3: error (exception set)
1672 * -2: not found (no exception set)
1710 * -2: not found (no exception set)
1673 * rest: valid rev
1711 * rest: valid rev
1674 */
1712 */
1675 static int index_find_node(indexObject *self,
1713 static int index_find_node(indexObject *self,
1676 const char *node, Py_ssize_t nodelen)
1714 const char *node, Py_ssize_t nodelen)
1677 {
1715 {
1678 int rev;
1716 int rev;
1679
1717
1680 self->ntlookups++;
1718 self->ntlookups++;
1681 rev = nt_find(self, node, nodelen, 0);
1719 rev = nt_find(self, node, nodelen, 0);
1682 if (rev >= -1)
1720 if (rev >= -1)
1683 return rev;
1721 return rev;
1684
1722
1685 if (nt_init(self) == -1)
1723 if (nt_init(self) == -1)
1686 return -3;
1724 return -3;
1687
1725
1688 /*
1726 /*
1689 * For the first handful of lookups, we scan the entire index,
1727 * For the first handful of lookups, we scan the entire index,
1690 * and cache only the matching nodes. This optimizes for cases
1728 * and cache only the matching nodes. This optimizes for cases
1691 * like "hg tip", where only a few nodes are accessed.
1729 * like "hg tip", where only a few nodes are accessed.
1692 *
1730 *
1693 * After that, we cache every node we visit, using a single
1731 * After that, we cache every node we visit, using a single
1694 * scan amortized over multiple lookups. This gives the best
1732 * scan amortized over multiple lookups. This gives the best
1695 * bulk performance, e.g. for "hg log".
1733 * bulk performance, e.g. for "hg log".
1696 */
1734 */
1697 if (self->ntmisses++ < 4) {
1735 if (self->ntmisses++ < 4) {
1698 for (rev = self->ntrev - 1; rev >= 0; rev--) {
1736 for (rev = self->ntrev - 1; rev >= 0; rev--) {
1699 const char *n = index_node(self, rev);
1737 const char *n = index_node(self, rev);
1700 if (n == NULL)
1738 if (n == NULL)
1701 return -2;
1739 return -2;
1702 if (memcmp(node, n, nodelen > 20 ? 20 : nodelen) == 0) {
1740 if (memcmp(node, n, nodelen > 20 ? 20 : nodelen) == 0) {
1703 if (nt_insert(self, n, rev) == -1)
1741 if (nt_insert(self, n, rev) == -1)
1704 return -3;
1742 return -3;
1705 break;
1743 break;
1706 }
1744 }
1707 }
1745 }
1708 } else {
1746 } else {
1709 for (rev = self->ntrev - 1; rev >= 0; rev--) {
1747 for (rev = self->ntrev - 1; rev >= 0; rev--) {
1710 const char *n = index_node(self, rev);
1748 const char *n = index_node(self, rev);
1711 if (n == NULL) {
1749 if (n == NULL) {
1712 self->ntrev = rev + 1;
1750 self->ntrev = rev + 1;
1713 return -2;
1751 return -2;
1714 }
1752 }
1715 if (nt_insert(self, n, rev) == -1) {
1753 if (nt_insert(self, n, rev) == -1) {
1716 self->ntrev = rev + 1;
1754 self->ntrev = rev + 1;
1717 return -3;
1755 return -3;
1718 }
1756 }
1719 if (memcmp(node, n, nodelen > 20 ? 20 : nodelen) == 0) {
1757 if (memcmp(node, n, nodelen > 20 ? 20 : nodelen) == 0) {
1720 break;
1758 break;
1721 }
1759 }
1722 }
1760 }
1723 self->ntrev = rev;
1761 self->ntrev = rev;
1724 }
1762 }
1725
1763
1726 if (rev >= 0)
1764 if (rev >= 0)
1727 return rev;
1765 return rev;
1728 return -2;
1766 return -2;
1729 }
1767 }
1730
1768
1731 static void raise_revlog_error(void)
1769 static void raise_revlog_error(void)
1732 {
1770 {
1733 PyObject *mod = NULL, *dict = NULL, *errclass = NULL;
1771 PyObject *mod = NULL, *dict = NULL, *errclass = NULL;
1734
1772
1735 mod = PyImport_ImportModule("mercurial.error");
1773 mod = PyImport_ImportModule("mercurial.error");
1736 if (mod == NULL) {
1774 if (mod == NULL) {
1737 goto cleanup;
1775 goto cleanup;
1738 }
1776 }
1739
1777
1740 dict = PyModule_GetDict(mod);
1778 dict = PyModule_GetDict(mod);
1741 if (dict == NULL) {
1779 if (dict == NULL) {
1742 goto cleanup;
1780 goto cleanup;
1743 }
1781 }
1744 Py_INCREF(dict);
1782 Py_INCREF(dict);
1745
1783
1746 errclass = PyDict_GetItemString(dict, "RevlogError");
1784 errclass = PyDict_GetItemString(dict, "RevlogError");
1747 if (errclass == NULL) {
1785 if (errclass == NULL) {
1748 PyErr_SetString(PyExc_SystemError,
1786 PyErr_SetString(PyExc_SystemError,
1749 "could not find RevlogError");
1787 "could not find RevlogError");
1750 goto cleanup;
1788 goto cleanup;
1751 }
1789 }
1752
1790
1753 /* value of exception is ignored by callers */
1791 /* value of exception is ignored by callers */
1754 PyErr_SetString(errclass, "RevlogError");
1792 PyErr_SetString(errclass, "RevlogError");
1755
1793
1756 cleanup:
1794 cleanup:
1757 Py_XDECREF(dict);
1795 Py_XDECREF(dict);
1758 Py_XDECREF(mod);
1796 Py_XDECREF(mod);
1759 }
1797 }
1760
1798
1761 static PyObject *index_getitem(indexObject *self, PyObject *value)
1799 static PyObject *index_getitem(indexObject *self, PyObject *value)
1762 {
1800 {
1763 char *node;
1801 char *node;
1764 Py_ssize_t nodelen;
1802 Py_ssize_t nodelen;
1765 int rev;
1803 int rev;
1766
1804
1767 if (PyInt_Check(value))
1805 if (PyInt_Check(value))
1768 return index_get(self, PyInt_AS_LONG(value));
1806 return index_get(self, PyInt_AS_LONG(value));
1769
1807
1770 if (node_check(value, &node, &nodelen) == -1)
1808 if (node_check(value, &node, &nodelen) == -1)
1771 return NULL;
1809 return NULL;
1772 rev = index_find_node(self, node, nodelen);
1810 rev = index_find_node(self, node, nodelen);
1773 if (rev >= -1)
1811 if (rev >= -1)
1774 return PyInt_FromLong(rev);
1812 return PyInt_FromLong(rev);
1775 if (rev == -2)
1813 if (rev == -2)
1776 raise_revlog_error();
1814 raise_revlog_error();
1777 return NULL;
1815 return NULL;
1778 }
1816 }
1779
1817
1780 static int nt_partialmatch(indexObject *self, const char *node,
1818 static int nt_partialmatch(indexObject *self, const char *node,
1781 Py_ssize_t nodelen)
1819 Py_ssize_t nodelen)
1782 {
1820 {
1783 int rev;
1821 int rev;
1784
1822
1785 if (nt_init(self) == -1)
1823 if (nt_init(self) == -1)
1786 return -3;
1824 return -3;
1787
1825
1788 if (self->ntrev > 0) {
1826 if (self->ntrev > 0) {
1789 /* ensure that the radix tree is fully populated */
1827 /* ensure that the radix tree is fully populated */
1790 for (rev = self->ntrev - 1; rev >= 0; rev--) {
1828 for (rev = self->ntrev - 1; rev >= 0; rev--) {
1791 const char *n = index_node(self, rev);
1829 const char *n = index_node(self, rev);
1792 if (n == NULL)
1830 if (n == NULL)
1793 return -2;
1831 return -2;
1794 if (nt_insert(self, n, rev) == -1)
1832 if (nt_insert(self, n, rev) == -1)
1795 return -3;
1833 return -3;
1796 }
1834 }
1797 self->ntrev = rev;
1835 self->ntrev = rev;
1798 }
1836 }
1799
1837
1800 return nt_find(self, node, nodelen, 1);
1838 return nt_find(self, node, nodelen, 1);
1801 }
1839 }
1802
1840
1803 static PyObject *index_partialmatch(indexObject *self, PyObject *args)
1841 static PyObject *index_partialmatch(indexObject *self, PyObject *args)
1804 {
1842 {
1805 const char *fullnode;
1843 const char *fullnode;
1806 int nodelen;
1844 int nodelen;
1807 char *node;
1845 char *node;
1808 int rev, i;
1846 int rev, i;
1809
1847
1810 if (!PyArg_ParseTuple(args, "s#", &node, &nodelen))
1848 if (!PyArg_ParseTuple(args, "s#", &node, &nodelen))
1811 return NULL;
1849 return NULL;
1812
1850
1813 if (nodelen < 4) {
1851 if (nodelen < 4) {
1814 PyErr_SetString(PyExc_ValueError, "key too short");
1852 PyErr_SetString(PyExc_ValueError, "key too short");
1815 return NULL;
1853 return NULL;
1816 }
1854 }
1817
1855
1818 if (nodelen > 40) {
1856 if (nodelen > 40) {
1819 PyErr_SetString(PyExc_ValueError, "key too long");
1857 PyErr_SetString(PyExc_ValueError, "key too long");
1820 return NULL;
1858 return NULL;
1821 }
1859 }
1822
1860
1823 for (i = 0; i < nodelen; i++)
1861 for (i = 0; i < nodelen; i++)
1824 hexdigit(node, i);
1862 hexdigit(node, i);
1825 if (PyErr_Occurred()) {
1863 if (PyErr_Occurred()) {
1826 /* input contains non-hex characters */
1864 /* input contains non-hex characters */
1827 PyErr_Clear();
1865 PyErr_Clear();
1828 Py_RETURN_NONE;
1866 Py_RETURN_NONE;
1829 }
1867 }
1830
1868
1831 rev = nt_partialmatch(self, node, nodelen);
1869 rev = nt_partialmatch(self, node, nodelen);
1832
1870
1833 switch (rev) {
1871 switch (rev) {
1834 case -4:
1872 case -4:
1835 raise_revlog_error();
1873 raise_revlog_error();
1836 case -3:
1874 case -3:
1837 return NULL;
1875 return NULL;
1838 case -2:
1876 case -2:
1839 Py_RETURN_NONE;
1877 Py_RETURN_NONE;
1840 case -1:
1878 case -1:
1841 return PyBytes_FromStringAndSize(nullid, 20);
1879 return PyBytes_FromStringAndSize(nullid, 20);
1842 }
1880 }
1843
1881
1844 fullnode = index_node(self, rev);
1882 fullnode = index_node(self, rev);
1845 if (fullnode == NULL) {
1883 if (fullnode == NULL) {
1846 PyErr_Format(PyExc_IndexError,
1884 PyErr_Format(PyExc_IndexError,
1847 "could not access rev %d", rev);
1885 "could not access rev %d", rev);
1848 return NULL;
1886 return NULL;
1849 }
1887 }
1850 return PyBytes_FromStringAndSize(fullnode, 20);
1888 return PyBytes_FromStringAndSize(fullnode, 20);
1851 }
1889 }
1852
1890
1853 static PyObject *index_m_get(indexObject *self, PyObject *args)
1891 static PyObject *index_m_get(indexObject *self, PyObject *args)
1854 {
1892 {
1855 Py_ssize_t nodelen;
1893 Py_ssize_t nodelen;
1856 PyObject *val;
1894 PyObject *val;
1857 char *node;
1895 char *node;
1858 int rev;
1896 int rev;
1859
1897
1860 if (!PyArg_ParseTuple(args, "O", &val))
1898 if (!PyArg_ParseTuple(args, "O", &val))
1861 return NULL;
1899 return NULL;
1862 if (node_check(val, &node, &nodelen) == -1)
1900 if (node_check(val, &node, &nodelen) == -1)
1863 return NULL;
1901 return NULL;
1864 rev = index_find_node(self, node, nodelen);
1902 rev = index_find_node(self, node, nodelen);
1865 if (rev == -3)
1903 if (rev == -3)
1866 return NULL;
1904 return NULL;
1867 if (rev == -2)
1905 if (rev == -2)
1868 Py_RETURN_NONE;
1906 Py_RETURN_NONE;
1869 return PyInt_FromLong(rev);
1907 return PyInt_FromLong(rev);
1870 }
1908 }
1871
1909
1872 static int index_contains(indexObject *self, PyObject *value)
1910 static int index_contains(indexObject *self, PyObject *value)
1873 {
1911 {
1874 char *node;
1912 char *node;
1875 Py_ssize_t nodelen;
1913 Py_ssize_t nodelen;
1876
1914
1877 if (PyInt_Check(value)) {
1915 if (PyInt_Check(value)) {
1878 long rev = PyInt_AS_LONG(value);
1916 long rev = PyInt_AS_LONG(value);
1879 return rev >= -1 && rev < index_length(self);
1917 return rev >= -1 && rev < index_length(self);
1880 }
1918 }
1881
1919
1882 if (node_check(value, &node, &nodelen) == -1)
1920 if (node_check(value, &node, &nodelen) == -1)
1883 return -1;
1921 return -1;
1884
1922
1885 switch (index_find_node(self, node, nodelen)) {
1923 switch (index_find_node(self, node, nodelen)) {
1886 case -3:
1924 case -3:
1887 return -1;
1925 return -1;
1888 case -2:
1926 case -2:
1889 return 0;
1927 return 0;
1890 default:
1928 default:
1891 return 1;
1929 return 1;
1892 }
1930 }
1893 }
1931 }
1894
1932
1895 typedef uint64_t bitmask;
1933 typedef uint64_t bitmask;
1896
1934
1897 /*
1935 /*
1898 * Given a disjoint set of revs, return all candidates for the
1936 * Given a disjoint set of revs, return all candidates for the
1899 * greatest common ancestor. In revset notation, this is the set
1937 * greatest common ancestor. In revset notation, this is the set
1900 * "heads(::a and ::b and ...)"
1938 * "heads(::a and ::b and ...)"
1901 */
1939 */
1902 static PyObject *find_gca_candidates(indexObject *self, const int *revs,
1940 static PyObject *find_gca_candidates(indexObject *self, const int *revs,
1903 int revcount)
1941 int revcount)
1904 {
1942 {
1905 const bitmask allseen = (1ull << revcount) - 1;
1943 const bitmask allseen = (1ull << revcount) - 1;
1906 const bitmask poison = 1ull << revcount;
1944 const bitmask poison = 1ull << revcount;
1907 PyObject *gca = PyList_New(0);
1945 PyObject *gca = PyList_New(0);
1908 int i, v, interesting;
1946 int i, v, interesting;
1909 int maxrev = -1;
1947 int maxrev = -1;
1910 bitmask sp;
1948 bitmask sp;
1911 bitmask *seen;
1949 bitmask *seen;
1912
1950
1913 if (gca == NULL)
1951 if (gca == NULL)
1914 return PyErr_NoMemory();
1952 return PyErr_NoMemory();
1915
1953
1916 for (i = 0; i < revcount; i++) {
1954 for (i = 0; i < revcount; i++) {
1917 if (revs[i] > maxrev)
1955 if (revs[i] > maxrev)
1918 maxrev = revs[i];
1956 maxrev = revs[i];
1919 }
1957 }
1920
1958
1921 seen = calloc(sizeof(*seen), maxrev + 1);
1959 seen = calloc(sizeof(*seen), maxrev + 1);
1922 if (seen == NULL) {
1960 if (seen == NULL) {
1923 Py_DECREF(gca);
1961 Py_DECREF(gca);
1924 return PyErr_NoMemory();
1962 return PyErr_NoMemory();
1925 }
1963 }
1926
1964
1927 for (i = 0; i < revcount; i++)
1965 for (i = 0; i < revcount; i++)
1928 seen[revs[i]] = 1ull << i;
1966 seen[revs[i]] = 1ull << i;
1929
1967
1930 interesting = revcount;
1968 interesting = revcount;
1931
1969
1932 for (v = maxrev; v >= 0 && interesting; v--) {
1970 for (v = maxrev; v >= 0 && interesting; v--) {
1933 bitmask sv = seen[v];
1971 bitmask sv = seen[v];
1934 int parents[2];
1972 int parents[2];
1935
1973
1936 if (!sv)
1974 if (!sv)
1937 continue;
1975 continue;
1938
1976
1939 if (sv < poison) {
1977 if (sv < poison) {
1940 interesting -= 1;
1978 interesting -= 1;
1941 if (sv == allseen) {
1979 if (sv == allseen) {
1942 PyObject *obj = PyInt_FromLong(v);
1980 PyObject *obj = PyInt_FromLong(v);
1943 if (obj == NULL)
1981 if (obj == NULL)
1944 goto bail;
1982 goto bail;
1945 if (PyList_Append(gca, obj) == -1) {
1983 if (PyList_Append(gca, obj) == -1) {
1946 Py_DECREF(obj);
1984 Py_DECREF(obj);
1947 goto bail;
1985 goto bail;
1948 }
1986 }
1949 sv |= poison;
1987 sv |= poison;
1950 for (i = 0; i < revcount; i++) {
1988 for (i = 0; i < revcount; i++) {
1951 if (revs[i] == v)
1989 if (revs[i] == v)
1952 goto done;
1990 goto done;
1953 }
1991 }
1954 }
1992 }
1955 }
1993 }
1956 if (index_get_parents(self, v, parents, maxrev) < 0)
1994 if (index_get_parents(self, v, parents, maxrev) < 0)
1957 goto bail;
1995 goto bail;
1958
1996
1959 for (i = 0; i < 2; i++) {
1997 for (i = 0; i < 2; i++) {
1960 int p = parents[i];
1998 int p = parents[i];
1961 if (p == -1)
1999 if (p == -1)
1962 continue;
2000 continue;
1963 sp = seen[p];
2001 sp = seen[p];
1964 if (sv < poison) {
2002 if (sv < poison) {
1965 if (sp == 0) {
2003 if (sp == 0) {
1966 seen[p] = sv;
2004 seen[p] = sv;
1967 interesting++;
2005 interesting++;
1968 }
2006 }
1969 else if (sp != sv)
2007 else if (sp != sv)
1970 seen[p] |= sv;
2008 seen[p] |= sv;
1971 } else {
2009 } else {
1972 if (sp && sp < poison)
2010 if (sp && sp < poison)
1973 interesting--;
2011 interesting--;
1974 seen[p] = sv;
2012 seen[p] = sv;
1975 }
2013 }
1976 }
2014 }
1977 }
2015 }
1978
2016
1979 done:
2017 done:
1980 free(seen);
2018 free(seen);
1981 return gca;
2019 return gca;
1982 bail:
2020 bail:
1983 free(seen);
2021 free(seen);
1984 Py_XDECREF(gca);
2022 Py_XDECREF(gca);
1985 return NULL;
2023 return NULL;
1986 }
2024 }
1987
2025
1988 /*
2026 /*
1989 * Given a disjoint set of revs, return the subset with the longest
2027 * Given a disjoint set of revs, return the subset with the longest
1990 * path to the root.
2028 * path to the root.
1991 */
2029 */
1992 static PyObject *find_deepest(indexObject *self, PyObject *revs)
2030 static PyObject *find_deepest(indexObject *self, PyObject *revs)
1993 {
2031 {
1994 const Py_ssize_t revcount = PyList_GET_SIZE(revs);
2032 const Py_ssize_t revcount = PyList_GET_SIZE(revs);
1995 static const Py_ssize_t capacity = 24;
2033 static const Py_ssize_t capacity = 24;
1996 int *depth, *interesting = NULL;
2034 int *depth, *interesting = NULL;
1997 int i, j, v, ninteresting;
2035 int i, j, v, ninteresting;
1998 PyObject *dict = NULL, *keys = NULL;
2036 PyObject *dict = NULL, *keys = NULL;
1999 long *seen = NULL;
2037 long *seen = NULL;
2000 int maxrev = -1;
2038 int maxrev = -1;
2001 long final;
2039 long final;
2002
2040
2003 if (revcount > capacity) {
2041 if (revcount > capacity) {
2004 PyErr_Format(PyExc_OverflowError,
2042 PyErr_Format(PyExc_OverflowError,
2005 "bitset size (%ld) > capacity (%ld)",
2043 "bitset size (%ld) > capacity (%ld)",
2006 (long)revcount, (long)capacity);
2044 (long)revcount, (long)capacity);
2007 return NULL;
2045 return NULL;
2008 }
2046 }
2009
2047
2010 for (i = 0; i < revcount; i++) {
2048 for (i = 0; i < revcount; i++) {
2011 int n = (int)PyInt_AsLong(PyList_GET_ITEM(revs, i));
2049 int n = (int)PyInt_AsLong(PyList_GET_ITEM(revs, i));
2012 if (n > maxrev)
2050 if (n > maxrev)
2013 maxrev = n;
2051 maxrev = n;
2014 }
2052 }
2015
2053
2016 depth = calloc(sizeof(*depth), maxrev + 1);
2054 depth = calloc(sizeof(*depth), maxrev + 1);
2017 if (depth == NULL)
2055 if (depth == NULL)
2018 return PyErr_NoMemory();
2056 return PyErr_NoMemory();
2019
2057
2020 seen = calloc(sizeof(*seen), maxrev + 1);
2058 seen = calloc(sizeof(*seen), maxrev + 1);
2021 if (seen == NULL) {
2059 if (seen == NULL) {
2022 PyErr_NoMemory();
2060 PyErr_NoMemory();
2023 goto bail;
2061 goto bail;
2024 }
2062 }
2025
2063
2026 interesting = calloc(sizeof(*interesting), 2 << revcount);
2064 interesting = calloc(sizeof(*interesting), 2 << revcount);
2027 if (interesting == NULL) {
2065 if (interesting == NULL) {
2028 PyErr_NoMemory();
2066 PyErr_NoMemory();
2029 goto bail;
2067 goto bail;
2030 }
2068 }
2031
2069
2032 if (PyList_Sort(revs) == -1)
2070 if (PyList_Sort(revs) == -1)
2033 goto bail;
2071 goto bail;
2034
2072
2035 for (i = 0; i < revcount; i++) {
2073 for (i = 0; i < revcount; i++) {
2036 int n = (int)PyInt_AsLong(PyList_GET_ITEM(revs, i));
2074 int n = (int)PyInt_AsLong(PyList_GET_ITEM(revs, i));
2037 long b = 1l << i;
2075 long b = 1l << i;
2038 depth[n] = 1;
2076 depth[n] = 1;
2039 seen[n] = b;
2077 seen[n] = b;
2040 interesting[b] = 1;
2078 interesting[b] = 1;
2041 }
2079 }
2042
2080
2043 ninteresting = (int)revcount;
2081 ninteresting = (int)revcount;
2044
2082
2045 for (v = maxrev; v >= 0 && ninteresting > 1; v--) {
2083 for (v = maxrev; v >= 0 && ninteresting > 1; v--) {
2046 int dv = depth[v];
2084 int dv = depth[v];
2047 int parents[2];
2085 int parents[2];
2048 long sv;
2086 long sv;
2049
2087
2050 if (dv == 0)
2088 if (dv == 0)
2051 continue;
2089 continue;
2052
2090
2053 sv = seen[v];
2091 sv = seen[v];
2054 if (index_get_parents(self, v, parents, maxrev) < 0)
2092 if (index_get_parents(self, v, parents, maxrev) < 0)
2055 goto bail;
2093 goto bail;
2056
2094
2057 for (i = 0; i < 2; i++) {
2095 for (i = 0; i < 2; i++) {
2058 int p = parents[i];
2096 int p = parents[i];
2059 long sp;
2097 long sp;
2060 int dp;
2098 int dp;
2061
2099
2062 if (p == -1)
2100 if (p == -1)
2063 continue;
2101 continue;
2064
2102
2065 dp = depth[p];
2103 dp = depth[p];
2066 sp = seen[p];
2104 sp = seen[p];
2067 if (dp <= dv) {
2105 if (dp <= dv) {
2068 depth[p] = dv + 1;
2106 depth[p] = dv + 1;
2069 if (sp != sv) {
2107 if (sp != sv) {
2070 interesting[sv] += 1;
2108 interesting[sv] += 1;
2071 seen[p] = sv;
2109 seen[p] = sv;
2072 if (sp) {
2110 if (sp) {
2073 interesting[sp] -= 1;
2111 interesting[sp] -= 1;
2074 if (interesting[sp] == 0)
2112 if (interesting[sp] == 0)
2075 ninteresting -= 1;
2113 ninteresting -= 1;
2076 }
2114 }
2077 }
2115 }
2078 }
2116 }
2079 else if (dv == dp - 1) {
2117 else if (dv == dp - 1) {
2080 long nsp = sp | sv;
2118 long nsp = sp | sv;
2081 if (nsp == sp)
2119 if (nsp == sp)
2082 continue;
2120 continue;
2083 seen[p] = nsp;
2121 seen[p] = nsp;
2084 interesting[sp] -= 1;
2122 interesting[sp] -= 1;
2085 if (interesting[sp] == 0 && interesting[nsp] > 0)
2123 if (interesting[sp] == 0 && interesting[nsp] > 0)
2086 ninteresting -= 1;
2124 ninteresting -= 1;
2087 interesting[nsp] += 1;
2125 interesting[nsp] += 1;
2088 }
2126 }
2089 }
2127 }
2090 interesting[sv] -= 1;
2128 interesting[sv] -= 1;
2091 if (interesting[sv] == 0)
2129 if (interesting[sv] == 0)
2092 ninteresting -= 1;
2130 ninteresting -= 1;
2093 }
2131 }
2094
2132
2095 final = 0;
2133 final = 0;
2096 j = ninteresting;
2134 j = ninteresting;
2097 for (i = 0; i < (int)(2 << revcount) && j > 0; i++) {
2135 for (i = 0; i < (int)(2 << revcount) && j > 0; i++) {
2098 if (interesting[i] == 0)
2136 if (interesting[i] == 0)
2099 continue;
2137 continue;
2100 final |= i;
2138 final |= i;
2101 j -= 1;
2139 j -= 1;
2102 }
2140 }
2103 if (final == 0) {
2141 if (final == 0) {
2104 keys = PyList_New(0);
2142 keys = PyList_New(0);
2105 goto bail;
2143 goto bail;
2106 }
2144 }
2107
2145
2108 dict = PyDict_New();
2146 dict = PyDict_New();
2109 if (dict == NULL)
2147 if (dict == NULL)
2110 goto bail;
2148 goto bail;
2111
2149
2112 for (i = 0; i < revcount; i++) {
2150 for (i = 0; i < revcount; i++) {
2113 PyObject *key;
2151 PyObject *key;
2114
2152
2115 if ((final & (1 << i)) == 0)
2153 if ((final & (1 << i)) == 0)
2116 continue;
2154 continue;
2117
2155
2118 key = PyList_GET_ITEM(revs, i);
2156 key = PyList_GET_ITEM(revs, i);
2119 Py_INCREF(key);
2157 Py_INCREF(key);
2120 Py_INCREF(Py_None);
2158 Py_INCREF(Py_None);
2121 if (PyDict_SetItem(dict, key, Py_None) == -1) {
2159 if (PyDict_SetItem(dict, key, Py_None) == -1) {
2122 Py_DECREF(key);
2160 Py_DECREF(key);
2123 Py_DECREF(Py_None);
2161 Py_DECREF(Py_None);
2124 goto bail;
2162 goto bail;
2125 }
2163 }
2126 }
2164 }
2127
2165
2128 keys = PyDict_Keys(dict);
2166 keys = PyDict_Keys(dict);
2129
2167
2130 bail:
2168 bail:
2131 free(depth);
2169 free(depth);
2132 free(seen);
2170 free(seen);
2133 free(interesting);
2171 free(interesting);
2134 Py_XDECREF(dict);
2172 Py_XDECREF(dict);
2135
2173
2136 return keys;
2174 return keys;
2137 }
2175 }
2138
2176
2139 /*
2177 /*
2140 * Given a (possibly overlapping) set of revs, return all the
2178 * Given a (possibly overlapping) set of revs, return all the
2141 * common ancestors heads: heads(::args[0] and ::a[1] and ...)
2179 * common ancestors heads: heads(::args[0] and ::a[1] and ...)
2142 */
2180 */
2143 static PyObject *index_commonancestorsheads(indexObject *self, PyObject *args)
2181 static PyObject *index_commonancestorsheads(indexObject *self, PyObject *args)
2144 {
2182 {
2145 PyObject *ret = NULL;
2183 PyObject *ret = NULL;
2146 Py_ssize_t argcount, i, len;
2184 Py_ssize_t argcount, i, len;
2147 bitmask repeat = 0;
2185 bitmask repeat = 0;
2148 int revcount = 0;
2186 int revcount = 0;
2149 int *revs;
2187 int *revs;
2150
2188
2151 argcount = PySequence_Length(args);
2189 argcount = PySequence_Length(args);
2152 revs = malloc(argcount * sizeof(*revs));
2190 revs = malloc(argcount * sizeof(*revs));
2153 if (argcount > 0 && revs == NULL)
2191 if (argcount > 0 && revs == NULL)
2154 return PyErr_NoMemory();
2192 return PyErr_NoMemory();
2155 len = index_length(self) - 1;
2193 len = index_length(self) - 1;
2156
2194
2157 for (i = 0; i < argcount; i++) {
2195 for (i = 0; i < argcount; i++) {
2158 static const int capacity = 24;
2196 static const int capacity = 24;
2159 PyObject *obj = PySequence_GetItem(args, i);
2197 PyObject *obj = PySequence_GetItem(args, i);
2160 bitmask x;
2198 bitmask x;
2161 long val;
2199 long val;
2162
2200
2163 if (!PyInt_Check(obj)) {
2201 if (!PyInt_Check(obj)) {
2164 PyErr_SetString(PyExc_TypeError,
2202 PyErr_SetString(PyExc_TypeError,
2165 "arguments must all be ints");
2203 "arguments must all be ints");
2166 Py_DECREF(obj);
2204 Py_DECREF(obj);
2167 goto bail;
2205 goto bail;
2168 }
2206 }
2169 val = PyInt_AsLong(obj);
2207 val = PyInt_AsLong(obj);
2170 Py_DECREF(obj);
2208 Py_DECREF(obj);
2171 if (val == -1) {
2209 if (val == -1) {
2172 ret = PyList_New(0);
2210 ret = PyList_New(0);
2173 goto done;
2211 goto done;
2174 }
2212 }
2175 if (val < 0 || val >= len) {
2213 if (val < 0 || val >= len) {
2176 PyErr_SetString(PyExc_IndexError,
2214 PyErr_SetString(PyExc_IndexError,
2177 "index out of range");
2215 "index out of range");
2178 goto bail;
2216 goto bail;
2179 }
2217 }
2180 /* this cheesy bloom filter lets us avoid some more
2218 /* this cheesy bloom filter lets us avoid some more
2181 * expensive duplicate checks in the common set-is-disjoint
2219 * expensive duplicate checks in the common set-is-disjoint
2182 * case */
2220 * case */
2183 x = 1ull << (val & 0x3f);
2221 x = 1ull << (val & 0x3f);
2184 if (repeat & x) {
2222 if (repeat & x) {
2185 int k;
2223 int k;
2186 for (k = 0; k < revcount; k++) {
2224 for (k = 0; k < revcount; k++) {
2187 if (val == revs[k])
2225 if (val == revs[k])
2188 goto duplicate;
2226 goto duplicate;
2189 }
2227 }
2190 }
2228 }
2191 else repeat |= x;
2229 else repeat |= x;
2192 if (revcount >= capacity) {
2230 if (revcount >= capacity) {
2193 PyErr_Format(PyExc_OverflowError,
2231 PyErr_Format(PyExc_OverflowError,
2194 "bitset size (%d) > capacity (%d)",
2232 "bitset size (%d) > capacity (%d)",
2195 revcount, capacity);
2233 revcount, capacity);
2196 goto bail;
2234 goto bail;
2197 }
2235 }
2198 revs[revcount++] = (int)val;
2236 revs[revcount++] = (int)val;
2199 duplicate:;
2237 duplicate:;
2200 }
2238 }
2201
2239
2202 if (revcount == 0) {
2240 if (revcount == 0) {
2203 ret = PyList_New(0);
2241 ret = PyList_New(0);
2204 goto done;
2242 goto done;
2205 }
2243 }
2206 if (revcount == 1) {
2244 if (revcount == 1) {
2207 PyObject *obj;
2245 PyObject *obj;
2208 ret = PyList_New(1);
2246 ret = PyList_New(1);
2209 if (ret == NULL)
2247 if (ret == NULL)
2210 goto bail;
2248 goto bail;
2211 obj = PyInt_FromLong(revs[0]);
2249 obj = PyInt_FromLong(revs[0]);
2212 if (obj == NULL)
2250 if (obj == NULL)
2213 goto bail;
2251 goto bail;
2214 PyList_SET_ITEM(ret, 0, obj);
2252 PyList_SET_ITEM(ret, 0, obj);
2215 goto done;
2253 goto done;
2216 }
2254 }
2217
2255
2218 ret = find_gca_candidates(self, revs, revcount);
2256 ret = find_gca_candidates(self, revs, revcount);
2219 if (ret == NULL)
2257 if (ret == NULL)
2220 goto bail;
2258 goto bail;
2221
2259
2222 done:
2260 done:
2223 free(revs);
2261 free(revs);
2224 return ret;
2262 return ret;
2225
2263
2226 bail:
2264 bail:
2227 free(revs);
2265 free(revs);
2228 Py_XDECREF(ret);
2266 Py_XDECREF(ret);
2229 return NULL;
2267 return NULL;
2230 }
2268 }
2231
2269
2232 /*
2270 /*
2233 * Given a (possibly overlapping) set of revs, return the greatest
2271 * Given a (possibly overlapping) set of revs, return the greatest
2234 * common ancestors: those with the longest path to the root.
2272 * common ancestors: those with the longest path to the root.
2235 */
2273 */
2236 static PyObject *index_ancestors(indexObject *self, PyObject *args)
2274 static PyObject *index_ancestors(indexObject *self, PyObject *args)
2237 {
2275 {
2238 PyObject *ret;
2276 PyObject *ret;
2239 PyObject *gca = index_commonancestorsheads(self, args);
2277 PyObject *gca = index_commonancestorsheads(self, args);
2240 if (gca == NULL)
2278 if (gca == NULL)
2241 return NULL;
2279 return NULL;
2242
2280
2243 if (PyList_GET_SIZE(gca) <= 1) {
2281 if (PyList_GET_SIZE(gca) <= 1) {
2244 return gca;
2282 return gca;
2245 }
2283 }
2246
2284
2247 ret = find_deepest(self, gca);
2285 ret = find_deepest(self, gca);
2248 Py_DECREF(gca);
2286 Py_DECREF(gca);
2249 return ret;
2287 return ret;
2250 }
2288 }
2251
2289
2252 /*
2290 /*
2253 * Invalidate any trie entries introduced by added revs.
2291 * Invalidate any trie entries introduced by added revs.
2254 */
2292 */
2255 static void nt_invalidate_added(indexObject *self, Py_ssize_t start)
2293 static void nt_invalidate_added(indexObject *self, Py_ssize_t start)
2256 {
2294 {
2257 Py_ssize_t i, len = PyList_GET_SIZE(self->added);
2295 Py_ssize_t i, len = PyList_GET_SIZE(self->added);
2258
2296
2259 for (i = start; i < len; i++) {
2297 for (i = start; i < len; i++) {
2260 PyObject *tuple = PyList_GET_ITEM(self->added, i);
2298 PyObject *tuple = PyList_GET_ITEM(self->added, i);
2261 PyObject *node = PyTuple_GET_ITEM(tuple, 7);
2299 PyObject *node = PyTuple_GET_ITEM(tuple, 7);
2262
2300
2263 nt_insert(self, PyBytes_AS_STRING(node), -1);
2301 nt_insert(self, PyBytes_AS_STRING(node), -1);
2264 }
2302 }
2265
2303
2266 if (start == 0)
2304 if (start == 0)
2267 Py_CLEAR(self->added);
2305 Py_CLEAR(self->added);
2268 }
2306 }
2269
2307
2270 /*
2308 /*
2271 * Delete a numeric range of revs, which must be at the end of the
2309 * Delete a numeric range of revs, which must be at the end of the
2272 * range, but exclude the sentinel nullid entry.
2310 * range, but exclude the sentinel nullid entry.
2273 */
2311 */
2274 static int index_slice_del(indexObject *self, PyObject *item)
2312 static int index_slice_del(indexObject *self, PyObject *item)
2275 {
2313 {
2276 Py_ssize_t start, stop, step, slicelength;
2314 Py_ssize_t start, stop, step, slicelength;
2277 Py_ssize_t length = index_length(self);
2315 Py_ssize_t length = index_length(self);
2278 int ret = 0;
2316 int ret = 0;
2279
2317
2280 /* Argument changed from PySliceObject* to PyObject* in Python 3. */
2318 /* Argument changed from PySliceObject* to PyObject* in Python 3. */
2281 #ifdef IS_PY3K
2319 #ifdef IS_PY3K
2282 if (PySlice_GetIndicesEx(item, length,
2320 if (PySlice_GetIndicesEx(item, length,
2283 #else
2321 #else
2284 if (PySlice_GetIndicesEx((PySliceObject*)item, length,
2322 if (PySlice_GetIndicesEx((PySliceObject*)item, length,
2285 #endif
2323 #endif
2286 &start, &stop, &step, &slicelength) < 0)
2324 &start, &stop, &step, &slicelength) < 0)
2287 return -1;
2325 return -1;
2288
2326
2289 if (slicelength <= 0)
2327 if (slicelength <= 0)
2290 return 0;
2328 return 0;
2291
2329
2292 if ((step < 0 && start < stop) || (step > 0 && start > stop))
2330 if ((step < 0 && start < stop) || (step > 0 && start > stop))
2293 stop = start;
2331 stop = start;
2294
2332
2295 if (step < 0) {
2333 if (step < 0) {
2296 stop = start + 1;
2334 stop = start + 1;
2297 start = stop + step*(slicelength - 1) - 1;
2335 start = stop + step*(slicelength - 1) - 1;
2298 step = -step;
2336 step = -step;
2299 }
2337 }
2300
2338
2301 if (step != 1) {
2339 if (step != 1) {
2302 PyErr_SetString(PyExc_ValueError,
2340 PyErr_SetString(PyExc_ValueError,
2303 "revlog index delete requires step size of 1");
2341 "revlog index delete requires step size of 1");
2304 return -1;
2342 return -1;
2305 }
2343 }
2306
2344
2307 if (stop != length - 1) {
2345 if (stop != length - 1) {
2308 PyErr_SetString(PyExc_IndexError,
2346 PyErr_SetString(PyExc_IndexError,
2309 "revlog index deletion indices are invalid");
2347 "revlog index deletion indices are invalid");
2310 return -1;
2348 return -1;
2311 }
2349 }
2312
2350
2313 if (start < self->length - 1) {
2351 if (start < self->length - 1) {
2314 if (self->nt) {
2352 if (self->nt) {
2315 Py_ssize_t i;
2353 Py_ssize_t i;
2316
2354
2317 for (i = start + 1; i < self->length - 1; i++) {
2355 for (i = start + 1; i < self->length - 1; i++) {
2318 const char *node = index_node(self, i);
2356 const char *node = index_node(self, i);
2319
2357
2320 if (node)
2358 if (node)
2321 nt_insert(self, node, -1);
2359 nt_insert(self, node, -1);
2322 }
2360 }
2323 if (self->added)
2361 if (self->added)
2324 nt_invalidate_added(self, 0);
2362 nt_invalidate_added(self, 0);
2325 if (self->ntrev > start)
2363 if (self->ntrev > start)
2326 self->ntrev = (int)start;
2364 self->ntrev = (int)start;
2327 }
2365 }
2328 self->length = start + 1;
2366 self->length = start + 1;
2329 if (start < self->raw_length) {
2367 if (start < self->raw_length) {
2330 if (self->cache) {
2368 if (self->cache) {
2331 Py_ssize_t i;
2369 Py_ssize_t i;
2332 for (i = start; i < self->raw_length; i++)
2370 for (i = start; i < self->raw_length; i++)
2333 Py_CLEAR(self->cache[i]);
2371 Py_CLEAR(self->cache[i]);
2334 }
2372 }
2335 self->raw_length = start;
2373 self->raw_length = start;
2336 }
2374 }
2337 goto done;
2375 goto done;
2338 }
2376 }
2339
2377
2340 if (self->nt) {
2378 if (self->nt) {
2341 nt_invalidate_added(self, start - self->length + 1);
2379 nt_invalidate_added(self, start - self->length + 1);
2342 if (self->ntrev > start)
2380 if (self->ntrev > start)
2343 self->ntrev = (int)start;
2381 self->ntrev = (int)start;
2344 }
2382 }
2345 if (self->added)
2383 if (self->added)
2346 ret = PyList_SetSlice(self->added, start - self->length + 1,
2384 ret = PyList_SetSlice(self->added, start - self->length + 1,
2347 PyList_GET_SIZE(self->added), NULL);
2385 PyList_GET_SIZE(self->added), NULL);
2348 done:
2386 done:
2349 Py_CLEAR(self->headrevs);
2387 Py_CLEAR(self->headrevs);
2350 return ret;
2388 return ret;
2351 }
2389 }
2352
2390
2353 /*
2391 /*
2354 * Supported ops:
2392 * Supported ops:
2355 *
2393 *
2356 * slice deletion
2394 * slice deletion
2357 * string assignment (extend node->rev mapping)
2395 * string assignment (extend node->rev mapping)
2358 * string deletion (shrink node->rev mapping)
2396 * string deletion (shrink node->rev mapping)
2359 */
2397 */
2360 static int index_assign_subscript(indexObject *self, PyObject *item,
2398 static int index_assign_subscript(indexObject *self, PyObject *item,
2361 PyObject *value)
2399 PyObject *value)
2362 {
2400 {
2363 char *node;
2401 char *node;
2364 Py_ssize_t nodelen;
2402 Py_ssize_t nodelen;
2365 long rev;
2403 long rev;
2366
2404
2367 if (PySlice_Check(item) && value == NULL)
2405 if (PySlice_Check(item) && value == NULL)
2368 return index_slice_del(self, item);
2406 return index_slice_del(self, item);
2369
2407
2370 if (node_check(item, &node, &nodelen) == -1)
2408 if (node_check(item, &node, &nodelen) == -1)
2371 return -1;
2409 return -1;
2372
2410
2373 if (value == NULL)
2411 if (value == NULL)
2374 return self->nt ? nt_insert(self, node, -1) : 0;
2412 return self->nt ? nt_insert(self, node, -1) : 0;
2375 rev = PyInt_AsLong(value);
2413 rev = PyInt_AsLong(value);
2376 if (rev > INT_MAX || rev < 0) {
2414 if (rev > INT_MAX || rev < 0) {
2377 if (!PyErr_Occurred())
2415 if (!PyErr_Occurred())
2378 PyErr_SetString(PyExc_ValueError, "rev out of range");
2416 PyErr_SetString(PyExc_ValueError, "rev out of range");
2379 return -1;
2417 return -1;
2380 }
2418 }
2381
2419
2382 if (nt_init(self) == -1)
2420 if (nt_init(self) == -1)
2383 return -1;
2421 return -1;
2384 return nt_insert(self, node, (int)rev);
2422 return nt_insert(self, node, (int)rev);
2385 }
2423 }
2386
2424
2387 /*
2425 /*
2388 * Find all RevlogNG entries in an index that has inline data. Update
2426 * Find all RevlogNG entries in an index that has inline data. Update
2389 * the optional "offsets" table with those entries.
2427 * the optional "offsets" table with those entries.
2390 */
2428 */
2391 static Py_ssize_t inline_scan(indexObject *self, const char **offsets)
2429 static Py_ssize_t inline_scan(indexObject *self, const char **offsets)
2392 {
2430 {
2393 const char *data = (const char *)self->buf.buf;
2431 const char *data = (const char *)self->buf.buf;
2394 Py_ssize_t pos = 0;
2432 Py_ssize_t pos = 0;
2395 Py_ssize_t end = self->buf.len;
2433 Py_ssize_t end = self->buf.len;
2396 long incr = v1_hdrsize;
2434 long incr = v1_hdrsize;
2397 Py_ssize_t len = 0;
2435 Py_ssize_t len = 0;
2398
2436
2399 while (pos + v1_hdrsize <= end && pos >= 0) {
2437 while (pos + v1_hdrsize <= end && pos >= 0) {
2400 uint32_t comp_len;
2438 uint32_t comp_len;
2401 /* 3rd element of header is length of compressed inline data */
2439 /* 3rd element of header is length of compressed inline data */
2402 comp_len = getbe32(data + pos + 8);
2440 comp_len = getbe32(data + pos + 8);
2403 incr = v1_hdrsize + comp_len;
2441 incr = v1_hdrsize + comp_len;
2404 if (offsets)
2442 if (offsets)
2405 offsets[len] = data + pos;
2443 offsets[len] = data + pos;
2406 len++;
2444 len++;
2407 pos += incr;
2445 pos += incr;
2408 }
2446 }
2409
2447
2410 if (pos != end) {
2448 if (pos != end) {
2411 if (!PyErr_Occurred())
2449 if (!PyErr_Occurred())
2412 PyErr_SetString(PyExc_ValueError, "corrupt index file");
2450 PyErr_SetString(PyExc_ValueError, "corrupt index file");
2413 return -1;
2451 return -1;
2414 }
2452 }
2415
2453
2416 return len;
2454 return len;
2417 }
2455 }
2418
2456
2419 static int index_init(indexObject *self, PyObject *args)
2457 static int index_init(indexObject *self, PyObject *args)
2420 {
2458 {
2421 PyObject *data_obj, *inlined_obj;
2459 PyObject *data_obj, *inlined_obj;
2422 Py_ssize_t size;
2460 Py_ssize_t size;
2423
2461
2424 /* Initialize before argument-checking to avoid index_dealloc() crash. */
2462 /* Initialize before argument-checking to avoid index_dealloc() crash. */
2425 self->raw_length = 0;
2463 self->raw_length = 0;
2426 self->added = NULL;
2464 self->added = NULL;
2427 self->cache = NULL;
2465 self->cache = NULL;
2428 self->data = NULL;
2466 self->data = NULL;
2429 memset(&self->buf, 0, sizeof(self->buf));
2467 memset(&self->buf, 0, sizeof(self->buf));
2430 self->headrevs = NULL;
2468 self->headrevs = NULL;
2431 self->filteredrevs = Py_None;
2469 self->filteredrevs = Py_None;
2432 Py_INCREF(Py_None);
2470 Py_INCREF(Py_None);
2433 self->nt = NULL;
2471 self->nt = NULL;
2434 self->offsets = NULL;
2472 self->offsets = NULL;
2435
2473
2436 if (!PyArg_ParseTuple(args, "OO", &data_obj, &inlined_obj))
2474 if (!PyArg_ParseTuple(args, "OO", &data_obj, &inlined_obj))
2437 return -1;
2475 return -1;
2438 if (!PyObject_CheckBuffer(data_obj)) {
2476 if (!PyObject_CheckBuffer(data_obj)) {
2439 PyErr_SetString(PyExc_TypeError,
2477 PyErr_SetString(PyExc_TypeError,
2440 "data does not support buffer interface");
2478 "data does not support buffer interface");
2441 return -1;
2479 return -1;
2442 }
2480 }
2443
2481
2444 if (PyObject_GetBuffer(data_obj, &self->buf, PyBUF_SIMPLE) == -1)
2482 if (PyObject_GetBuffer(data_obj, &self->buf, PyBUF_SIMPLE) == -1)
2445 return -1;
2483 return -1;
2446 size = self->buf.len;
2484 size = self->buf.len;
2447
2485
2448 self->inlined = inlined_obj && PyObject_IsTrue(inlined_obj);
2486 self->inlined = inlined_obj && PyObject_IsTrue(inlined_obj);
2449 self->data = data_obj;
2487 self->data = data_obj;
2450
2488
2451 self->ntlength = self->ntcapacity = 0;
2489 self->ntlength = self->ntcapacity = 0;
2452 self->ntdepth = self->ntsplits = 0;
2490 self->ntdepth = self->ntsplits = 0;
2453 self->ntlookups = self->ntmisses = 0;
2491 self->ntlookups = self->ntmisses = 0;
2454 self->ntrev = -1;
2492 self->ntrev = -1;
2455 Py_INCREF(self->data);
2493 Py_INCREF(self->data);
2456
2494
2457 if (self->inlined) {
2495 if (self->inlined) {
2458 Py_ssize_t len = inline_scan(self, NULL);
2496 Py_ssize_t len = inline_scan(self, NULL);
2459 if (len == -1)
2497 if (len == -1)
2460 goto bail;
2498 goto bail;
2461 self->raw_length = len;
2499 self->raw_length = len;
2462 self->length = len + 1;
2500 self->length = len + 1;
2463 } else {
2501 } else {
2464 if (size % v1_hdrsize) {
2502 if (size % v1_hdrsize) {
2465 PyErr_SetString(PyExc_ValueError, "corrupt index file");
2503 PyErr_SetString(PyExc_ValueError, "corrupt index file");
2466 goto bail;
2504 goto bail;
2467 }
2505 }
2468 self->raw_length = size / v1_hdrsize;
2506 self->raw_length = size / v1_hdrsize;
2469 self->length = self->raw_length + 1;
2507 self->length = self->raw_length + 1;
2470 }
2508 }
2471
2509
2472 return 0;
2510 return 0;
2473 bail:
2511 bail:
2474 return -1;
2512 return -1;
2475 }
2513 }
2476
2514
2477 static PyObject *index_nodemap(indexObject *self)
2515 static PyObject *index_nodemap(indexObject *self)
2478 {
2516 {
2479 Py_INCREF(self);
2517 Py_INCREF(self);
2480 return (PyObject *)self;
2518 return (PyObject *)self;
2481 }
2519 }
2482
2520
2483 static void index_dealloc(indexObject *self)
2521 static void index_dealloc(indexObject *self)
2484 {
2522 {
2485 _index_clearcaches(self);
2523 _index_clearcaches(self);
2486 Py_XDECREF(self->filteredrevs);
2524 Py_XDECREF(self->filteredrevs);
2487 if (self->buf.buf) {
2525 if (self->buf.buf) {
2488 PyBuffer_Release(&self->buf);
2526 PyBuffer_Release(&self->buf);
2489 memset(&self->buf, 0, sizeof(self->buf));
2527 memset(&self->buf, 0, sizeof(self->buf));
2490 }
2528 }
2491 Py_XDECREF(self->data);
2529 Py_XDECREF(self->data);
2492 Py_XDECREF(self->added);
2530 Py_XDECREF(self->added);
2493 PyObject_Del(self);
2531 PyObject_Del(self);
2494 }
2532 }
2495
2533
2496 static PySequenceMethods index_sequence_methods = {
2534 static PySequenceMethods index_sequence_methods = {
2497 (lenfunc)index_length, /* sq_length */
2535 (lenfunc)index_length, /* sq_length */
2498 0, /* sq_concat */
2536 0, /* sq_concat */
2499 0, /* sq_repeat */
2537 0, /* sq_repeat */
2500 (ssizeargfunc)index_get, /* sq_item */
2538 (ssizeargfunc)index_get, /* sq_item */
2501 0, /* sq_slice */
2539 0, /* sq_slice */
2502 0, /* sq_ass_item */
2540 0, /* sq_ass_item */
2503 0, /* sq_ass_slice */
2541 0, /* sq_ass_slice */
2504 (objobjproc)index_contains, /* sq_contains */
2542 (objobjproc)index_contains, /* sq_contains */
2505 };
2543 };
2506
2544
2507 static PyMappingMethods index_mapping_methods = {
2545 static PyMappingMethods index_mapping_methods = {
2508 (lenfunc)index_length, /* mp_length */
2546 (lenfunc)index_length, /* mp_length */
2509 (binaryfunc)index_getitem, /* mp_subscript */
2547 (binaryfunc)index_getitem, /* mp_subscript */
2510 (objobjargproc)index_assign_subscript, /* mp_ass_subscript */
2548 (objobjargproc)index_assign_subscript, /* mp_ass_subscript */
2511 };
2549 };
2512
2550
2513 static PyMethodDef index_methods[] = {
2551 static PyMethodDef index_methods[] = {
2514 {"ancestors", (PyCFunction)index_ancestors, METH_VARARGS,
2552 {"ancestors", (PyCFunction)index_ancestors, METH_VARARGS,
2515 "return the gca set of the given revs"},
2553 "return the gca set of the given revs"},
2516 {"commonancestorsheads", (PyCFunction)index_commonancestorsheads,
2554 {"commonancestorsheads", (PyCFunction)index_commonancestorsheads,
2517 METH_VARARGS,
2555 METH_VARARGS,
2518 "return the heads of the common ancestors of the given revs"},
2556 "return the heads of the common ancestors of the given revs"},
2519 {"clearcaches", (PyCFunction)index_clearcaches, METH_NOARGS,
2557 {"clearcaches", (PyCFunction)index_clearcaches, METH_NOARGS,
2520 "clear the index caches"},
2558 "clear the index caches"},
2521 {"get", (PyCFunction)index_m_get, METH_VARARGS,
2559 {"get", (PyCFunction)index_m_get, METH_VARARGS,
2522 "get an index entry"},
2560 "get an index entry"},
2523 {"computephasesmapsets", (PyCFunction)compute_phases_map_sets,
2561 {"computephasesmapsets", (PyCFunction)compute_phases_map_sets,
2524 METH_VARARGS, "compute phases"},
2562 METH_VARARGS, "compute phases"},
2525 {"reachableroots2", (PyCFunction)reachableroots2, METH_VARARGS,
2563 {"reachableroots2", (PyCFunction)reachableroots2, METH_VARARGS,
2526 "reachableroots"},
2564 "reachableroots"},
2527 {"headrevs", (PyCFunction)index_headrevs, METH_VARARGS,
2565 {"headrevs", (PyCFunction)index_headrevs, METH_VARARGS,
2528 "get head revisions"}, /* Can do filtering since 3.2 */
2566 "get head revisions"}, /* Can do filtering since 3.2 */
2529 {"headrevsfiltered", (PyCFunction)index_headrevs, METH_VARARGS,
2567 {"headrevsfiltered", (PyCFunction)index_headrevs, METH_VARARGS,
2530 "get filtered head revisions"}, /* Can always do filtering */
2568 "get filtered head revisions"}, /* Can always do filtering */
2531 {"insert", (PyCFunction)index_insert, METH_VARARGS,
2569 {"insert", (PyCFunction)index_insert, METH_VARARGS,
2532 "insert an index entry"},
2570 "insert an index entry"},
2533 {"partialmatch", (PyCFunction)index_partialmatch, METH_VARARGS,
2571 {"partialmatch", (PyCFunction)index_partialmatch, METH_VARARGS,
2534 "match a potentially ambiguous node ID"},
2572 "match a potentially ambiguous node ID"},
2535 {"stats", (PyCFunction)index_stats, METH_NOARGS,
2573 {"stats", (PyCFunction)index_stats, METH_NOARGS,
2536 "stats for the index"},
2574 "stats for the index"},
2537 {NULL} /* Sentinel */
2575 {NULL} /* Sentinel */
2538 };
2576 };
2539
2577
2540 static PyGetSetDef index_getset[] = {
2578 static PyGetSetDef index_getset[] = {
2541 {"nodemap", (getter)index_nodemap, NULL, "nodemap", NULL},
2579 {"nodemap", (getter)index_nodemap, NULL, "nodemap", NULL},
2542 {NULL} /* Sentinel */
2580 {NULL} /* Sentinel */
2543 };
2581 };
2544
2582
2545 static PyTypeObject indexType = {
2583 static PyTypeObject indexType = {
2546 PyVarObject_HEAD_INIT(NULL, 0)
2584 PyVarObject_HEAD_INIT(NULL, 0)
2547 "parsers.index", /* tp_name */
2585 "parsers.index", /* tp_name */
2548 sizeof(indexObject), /* tp_basicsize */
2586 sizeof(indexObject), /* tp_basicsize */
2549 0, /* tp_itemsize */
2587 0, /* tp_itemsize */
2550 (destructor)index_dealloc, /* tp_dealloc */
2588 (destructor)index_dealloc, /* tp_dealloc */
2551 0, /* tp_print */
2589 0, /* tp_print */
2552 0, /* tp_getattr */
2590 0, /* tp_getattr */
2553 0, /* tp_setattr */
2591 0, /* tp_setattr */
2554 0, /* tp_compare */
2592 0, /* tp_compare */
2555 0, /* tp_repr */
2593 0, /* tp_repr */
2556 0, /* tp_as_number */
2594 0, /* tp_as_number */
2557 &index_sequence_methods, /* tp_as_sequence */
2595 &index_sequence_methods, /* tp_as_sequence */
2558 &index_mapping_methods, /* tp_as_mapping */
2596 &index_mapping_methods, /* tp_as_mapping */
2559 0, /* tp_hash */
2597 0, /* tp_hash */
2560 0, /* tp_call */
2598 0, /* tp_call */
2561 0, /* tp_str */
2599 0, /* tp_str */
2562 0, /* tp_getattro */
2600 0, /* tp_getattro */
2563 0, /* tp_setattro */
2601 0, /* tp_setattro */
2564 0, /* tp_as_buffer */
2602 0, /* tp_as_buffer */
2565 Py_TPFLAGS_DEFAULT, /* tp_flags */
2603 Py_TPFLAGS_DEFAULT, /* tp_flags */
2566 "revlog index", /* tp_doc */
2604 "revlog index", /* tp_doc */
2567 0, /* tp_traverse */
2605 0, /* tp_traverse */
2568 0, /* tp_clear */
2606 0, /* tp_clear */
2569 0, /* tp_richcompare */
2607 0, /* tp_richcompare */
2570 0, /* tp_weaklistoffset */
2608 0, /* tp_weaklistoffset */
2571 0, /* tp_iter */
2609 0, /* tp_iter */
2572 0, /* tp_iternext */
2610 0, /* tp_iternext */
2573 index_methods, /* tp_methods */
2611 index_methods, /* tp_methods */
2574 0, /* tp_members */
2612 0, /* tp_members */
2575 index_getset, /* tp_getset */
2613 index_getset, /* tp_getset */
2576 0, /* tp_base */
2614 0, /* tp_base */
2577 0, /* tp_dict */
2615 0, /* tp_dict */
2578 0, /* tp_descr_get */
2616 0, /* tp_descr_get */
2579 0, /* tp_descr_set */
2617 0, /* tp_descr_set */
2580 0, /* tp_dictoffset */
2618 0, /* tp_dictoffset */
2581 (initproc)index_init, /* tp_init */
2619 (initproc)index_init, /* tp_init */
2582 0, /* tp_alloc */
2620 0, /* tp_alloc */
2583 };
2621 };
2584
2622
2585 /*
2623 /*
2586 * returns a tuple of the form (index, index, cache) with elements as
2624 * returns a tuple of the form (index, index, cache) with elements as
2587 * follows:
2625 * follows:
2588 *
2626 *
2589 * index: an index object that lazily parses RevlogNG records
2627 * index: an index object that lazily parses RevlogNG records
2590 * cache: if data is inlined, a tuple (0, index_file_content), else None
2628 * cache: if data is inlined, a tuple (0, index_file_content), else None
2591 * index_file_content could be a string, or a buffer
2629 * index_file_content could be a string, or a buffer
2592 *
2630 *
2593 * added complications are for backwards compatibility
2631 * added complications are for backwards compatibility
2594 */
2632 */
2595 static PyObject *parse_index2(PyObject *self, PyObject *args)
2633 static PyObject *parse_index2(PyObject *self, PyObject *args)
2596 {
2634 {
2597 PyObject *tuple = NULL, *cache = NULL;
2635 PyObject *tuple = NULL, *cache = NULL;
2598 indexObject *idx;
2636 indexObject *idx;
2599 int ret;
2637 int ret;
2600
2638
2601 idx = PyObject_New(indexObject, &indexType);
2639 idx = PyObject_New(indexObject, &indexType);
2602 if (idx == NULL)
2640 if (idx == NULL)
2603 goto bail;
2641 goto bail;
2604
2642
2605 ret = index_init(idx, args);
2643 ret = index_init(idx, args);
2606 if (ret == -1)
2644 if (ret == -1)
2607 goto bail;
2645 goto bail;
2608
2646
2609 if (idx->inlined) {
2647 if (idx->inlined) {
2610 cache = Py_BuildValue("iO", 0, idx->data);
2648 cache = Py_BuildValue("iO", 0, idx->data);
2611 if (cache == NULL)
2649 if (cache == NULL)
2612 goto bail;
2650 goto bail;
2613 } else {
2651 } else {
2614 cache = Py_None;
2652 cache = Py_None;
2615 Py_INCREF(cache);
2653 Py_INCREF(cache);
2616 }
2654 }
2617
2655
2618 tuple = Py_BuildValue("NN", idx, cache);
2656 tuple = Py_BuildValue("NN", idx, cache);
2619 if (!tuple)
2657 if (!tuple)
2620 goto bail;
2658 goto bail;
2621 return tuple;
2659 return tuple;
2622
2660
2623 bail:
2661 bail:
2624 Py_XDECREF(idx);
2662 Py_XDECREF(idx);
2625 Py_XDECREF(cache);
2663 Py_XDECREF(cache);
2626 Py_XDECREF(tuple);
2664 Py_XDECREF(tuple);
2627 return NULL;
2665 return NULL;
2628 }
2666 }
2629
2667
2630 #define BUMPED_FIX 1
2668 #define BUMPED_FIX 1
2631 #define USING_SHA_256 2
2669 #define USING_SHA_256 2
2632 #define FM1_HEADER_SIZE (4 + 8 + 2 + 2 + 1 + 1 + 1)
2670 #define FM1_HEADER_SIZE (4 + 8 + 2 + 2 + 1 + 1 + 1)
2633
2671
2634 static PyObject *readshas(
2672 static PyObject *readshas(
2635 const char *source, unsigned char num, Py_ssize_t hashwidth)
2673 const char *source, unsigned char num, Py_ssize_t hashwidth)
2636 {
2674 {
2637 int i;
2675 int i;
2638 PyObject *list = PyTuple_New(num);
2676 PyObject *list = PyTuple_New(num);
2639 if (list == NULL) {
2677 if (list == NULL) {
2640 return NULL;
2678 return NULL;
2641 }
2679 }
2642 for (i = 0; i < num; i++) {
2680 for (i = 0; i < num; i++) {
2643 PyObject *hash = PyBytes_FromStringAndSize(source, hashwidth);
2681 PyObject *hash = PyBytes_FromStringAndSize(source, hashwidth);
2644 if (hash == NULL) {
2682 if (hash == NULL) {
2645 Py_DECREF(list);
2683 Py_DECREF(list);
2646 return NULL;
2684 return NULL;
2647 }
2685 }
2648 PyTuple_SET_ITEM(list, i, hash);
2686 PyTuple_SET_ITEM(list, i, hash);
2649 source += hashwidth;
2687 source += hashwidth;
2650 }
2688 }
2651 return list;
2689 return list;
2652 }
2690 }
2653
2691
2654 static PyObject *fm1readmarker(const char *databegin, const char *dataend,
2692 static PyObject *fm1readmarker(const char *databegin, const char *dataend,
2655 uint32_t *msize)
2693 uint32_t *msize)
2656 {
2694 {
2657 const char *data = databegin;
2695 const char *data = databegin;
2658 const char *meta;
2696 const char *meta;
2659
2697
2660 double mtime;
2698 double mtime;
2661 int16_t tz;
2699 int16_t tz;
2662 uint16_t flags;
2700 uint16_t flags;
2663 unsigned char nsuccs, nparents, nmetadata;
2701 unsigned char nsuccs, nparents, nmetadata;
2664 Py_ssize_t hashwidth = 20;
2702 Py_ssize_t hashwidth = 20;
2665
2703
2666 PyObject *prec = NULL, *parents = NULL, *succs = NULL;
2704 PyObject *prec = NULL, *parents = NULL, *succs = NULL;
2667 PyObject *metadata = NULL, *ret = NULL;
2705 PyObject *metadata = NULL, *ret = NULL;
2668 int i;
2706 int i;
2669
2707
2670 if (data + FM1_HEADER_SIZE > dataend) {
2708 if (data + FM1_HEADER_SIZE > dataend) {
2671 goto overflow;
2709 goto overflow;
2672 }
2710 }
2673
2711
2674 *msize = getbe32(data);
2712 *msize = getbe32(data);
2675 data += 4;
2713 data += 4;
2676 mtime = getbefloat64(data);
2714 mtime = getbefloat64(data);
2677 data += 8;
2715 data += 8;
2678 tz = getbeint16(data);
2716 tz = getbeint16(data);
2679 data += 2;
2717 data += 2;
2680 flags = getbeuint16(data);
2718 flags = getbeuint16(data);
2681 data += 2;
2719 data += 2;
2682
2720
2683 if (flags & USING_SHA_256) {
2721 if (flags & USING_SHA_256) {
2684 hashwidth = 32;
2722 hashwidth = 32;
2685 }
2723 }
2686
2724
2687 nsuccs = (unsigned char)(*data++);
2725 nsuccs = (unsigned char)(*data++);
2688 nparents = (unsigned char)(*data++);
2726 nparents = (unsigned char)(*data++);
2689 nmetadata = (unsigned char)(*data++);
2727 nmetadata = (unsigned char)(*data++);
2690
2728
2691 if (databegin + *msize > dataend) {
2729 if (databegin + *msize > dataend) {
2692 goto overflow;
2730 goto overflow;
2693 }
2731 }
2694 dataend = databegin + *msize; /* narrow down to marker size */
2732 dataend = databegin + *msize; /* narrow down to marker size */
2695
2733
2696 if (data + hashwidth > dataend) {
2734 if (data + hashwidth > dataend) {
2697 goto overflow;
2735 goto overflow;
2698 }
2736 }
2699 prec = PyBytes_FromStringAndSize(data, hashwidth);
2737 prec = PyBytes_FromStringAndSize(data, hashwidth);
2700 data += hashwidth;
2738 data += hashwidth;
2701 if (prec == NULL) {
2739 if (prec == NULL) {
2702 goto bail;
2740 goto bail;
2703 }
2741 }
2704
2742
2705 if (data + nsuccs * hashwidth > dataend) {
2743 if (data + nsuccs * hashwidth > dataend) {
2706 goto overflow;
2744 goto overflow;
2707 }
2745 }
2708 succs = readshas(data, nsuccs, hashwidth);
2746 succs = readshas(data, nsuccs, hashwidth);
2709 if (succs == NULL) {
2747 if (succs == NULL) {
2710 goto bail;
2748 goto bail;
2711 }
2749 }
2712 data += nsuccs * hashwidth;
2750 data += nsuccs * hashwidth;
2713
2751
2714 if (nparents == 1 || nparents == 2) {
2752 if (nparents == 1 || nparents == 2) {
2715 if (data + nparents * hashwidth > dataend) {
2753 if (data + nparents * hashwidth > dataend) {
2716 goto overflow;
2754 goto overflow;
2717 }
2755 }
2718 parents = readshas(data, nparents, hashwidth);
2756 parents = readshas(data, nparents, hashwidth);
2719 if (parents == NULL) {
2757 if (parents == NULL) {
2720 goto bail;
2758 goto bail;
2721 }
2759 }
2722 data += nparents * hashwidth;
2760 data += nparents * hashwidth;
2723 } else {
2761 } else {
2724 parents = Py_None;
2762 parents = Py_None;
2725 }
2763 }
2726
2764
2727 if (data + 2 * nmetadata > dataend) {
2765 if (data + 2 * nmetadata > dataend) {
2728 goto overflow;
2766 goto overflow;
2729 }
2767 }
2730 meta = data + (2 * nmetadata);
2768 meta = data + (2 * nmetadata);
2731 metadata = PyTuple_New(nmetadata);
2769 metadata = PyTuple_New(nmetadata);
2732 if (metadata == NULL) {
2770 if (metadata == NULL) {
2733 goto bail;
2771 goto bail;
2734 }
2772 }
2735 for (i = 0; i < nmetadata; i++) {
2773 for (i = 0; i < nmetadata; i++) {
2736 PyObject *tmp, *left = NULL, *right = NULL;
2774 PyObject *tmp, *left = NULL, *right = NULL;
2737 Py_ssize_t leftsize = (unsigned char)(*data++);
2775 Py_ssize_t leftsize = (unsigned char)(*data++);
2738 Py_ssize_t rightsize = (unsigned char)(*data++);
2776 Py_ssize_t rightsize = (unsigned char)(*data++);
2739 if (meta + leftsize + rightsize > dataend) {
2777 if (meta + leftsize + rightsize > dataend) {
2740 goto overflow;
2778 goto overflow;
2741 }
2779 }
2742 left = PyBytes_FromStringAndSize(meta, leftsize);
2780 left = PyBytes_FromStringAndSize(meta, leftsize);
2743 meta += leftsize;
2781 meta += leftsize;
2744 right = PyBytes_FromStringAndSize(meta, rightsize);
2782 right = PyBytes_FromStringAndSize(meta, rightsize);
2745 meta += rightsize;
2783 meta += rightsize;
2746 tmp = PyTuple_New(2);
2784 tmp = PyTuple_New(2);
2747 if (!left || !right || !tmp) {
2785 if (!left || !right || !tmp) {
2748 Py_XDECREF(left);
2786 Py_XDECREF(left);
2749 Py_XDECREF(right);
2787 Py_XDECREF(right);
2750 Py_XDECREF(tmp);
2788 Py_XDECREF(tmp);
2751 goto bail;
2789 goto bail;
2752 }
2790 }
2753 PyTuple_SET_ITEM(tmp, 0, left);
2791 PyTuple_SET_ITEM(tmp, 0, left);
2754 PyTuple_SET_ITEM(tmp, 1, right);
2792 PyTuple_SET_ITEM(tmp, 1, right);
2755 PyTuple_SET_ITEM(metadata, i, tmp);
2793 PyTuple_SET_ITEM(metadata, i, tmp);
2756 }
2794 }
2757 ret = Py_BuildValue("(OOHO(di)O)", prec, succs, flags,
2795 ret = Py_BuildValue("(OOHO(di)O)", prec, succs, flags,
2758 metadata, mtime, (int)tz * 60, parents);
2796 metadata, mtime, (int)tz * 60, parents);
2759 goto bail; /* return successfully */
2797 goto bail; /* return successfully */
2760
2798
2761 overflow:
2799 overflow:
2762 PyErr_SetString(PyExc_ValueError, "overflow in obsstore");
2800 PyErr_SetString(PyExc_ValueError, "overflow in obsstore");
2763 bail:
2801 bail:
2764 Py_XDECREF(prec);
2802 Py_XDECREF(prec);
2765 Py_XDECREF(succs);
2803 Py_XDECREF(succs);
2766 Py_XDECREF(metadata);
2804 Py_XDECREF(metadata);
2767 if (parents != Py_None)
2805 if (parents != Py_None)
2768 Py_XDECREF(parents);
2806 Py_XDECREF(parents);
2769 return ret;
2807 return ret;
2770 }
2808 }
2771
2809
2772
2810
2773 static PyObject *fm1readmarkers(PyObject *self, PyObject *args) {
2811 static PyObject *fm1readmarkers(PyObject *self, PyObject *args) {
2774 const char *data, *dataend;
2812 const char *data, *dataend;
2775 int datalen;
2813 int datalen;
2776 Py_ssize_t offset, stop;
2814 Py_ssize_t offset, stop;
2777 PyObject *markers = NULL;
2815 PyObject *markers = NULL;
2778
2816
2779 if (!PyArg_ParseTuple(args, "s#nn", &data, &datalen, &offset, &stop)) {
2817 if (!PyArg_ParseTuple(args, "s#nn", &data, &datalen, &offset, &stop)) {
2780 return NULL;
2818 return NULL;
2781 }
2819 }
2782 dataend = data + datalen;
2820 dataend = data + datalen;
2783 data += offset;
2821 data += offset;
2784 markers = PyList_New(0);
2822 markers = PyList_New(0);
2785 if (!markers) {
2823 if (!markers) {
2786 return NULL;
2824 return NULL;
2787 }
2825 }
2788 while (offset < stop) {
2826 while (offset < stop) {
2789 uint32_t msize;
2827 uint32_t msize;
2790 int error;
2828 int error;
2791 PyObject *record = fm1readmarker(data, dataend, &msize);
2829 PyObject *record = fm1readmarker(data, dataend, &msize);
2792 if (!record) {
2830 if (!record) {
2793 goto bail;
2831 goto bail;
2794 }
2832 }
2795 error = PyList_Append(markers, record);
2833 error = PyList_Append(markers, record);
2796 Py_DECREF(record);
2834 Py_DECREF(record);
2797 if (error) {
2835 if (error) {
2798 goto bail;
2836 goto bail;
2799 }
2837 }
2800 data += msize;
2838 data += msize;
2801 offset += msize;
2839 offset += msize;
2802 }
2840 }
2803 return markers;
2841 return markers;
2804 bail:
2842 bail:
2805 Py_DECREF(markers);
2843 Py_DECREF(markers);
2806 return NULL;
2844 return NULL;
2807 }
2845 }
2808
2846
2809 static char parsers_doc[] = "Efficient content parsing.";
2847 static char parsers_doc[] = "Efficient content parsing.";
2810
2848
2811 PyObject *encodedir(PyObject *self, PyObject *args);
2849 PyObject *encodedir(PyObject *self, PyObject *args);
2812 PyObject *pathencode(PyObject *self, PyObject *args);
2850 PyObject *pathencode(PyObject *self, PyObject *args);
2813 PyObject *lowerencode(PyObject *self, PyObject *args);
2851 PyObject *lowerencode(PyObject *self, PyObject *args);
2814
2852
2815 static PyMethodDef methods[] = {
2853 static PyMethodDef methods[] = {
2816 {"pack_dirstate", pack_dirstate, METH_VARARGS, "pack a dirstate\n"},
2854 {"pack_dirstate", pack_dirstate, METH_VARARGS, "pack a dirstate\n"},
2817 {"nonnormalentries", nonnormalentries, METH_VARARGS,
2855 {"nonnormalentries", nonnormalentries, METH_VARARGS,
2818 "create a set containing non-normal entries of given dirstate\n"},
2856 "create a set containing non-normal entries of given dirstate\n"},
2857 {"nonnormalotherparententries", nonnormalotherparententries, METH_VARARGS,
2858 "create a set containing non-normal and other parent entries of given "
2859 "dirstate\n"},
2819 {"parse_manifest", parse_manifest, METH_VARARGS, "parse a manifest\n"},
2860 {"parse_manifest", parse_manifest, METH_VARARGS, "parse a manifest\n"},
2820 {"parse_dirstate", parse_dirstate, METH_VARARGS, "parse a dirstate\n"},
2861 {"parse_dirstate", parse_dirstate, METH_VARARGS, "parse a dirstate\n"},
2821 {"parse_index2", parse_index2, METH_VARARGS, "parse a revlog index\n"},
2862 {"parse_index2", parse_index2, METH_VARARGS, "parse a revlog index\n"},
2822 {"asciilower", asciilower, METH_VARARGS, "lowercase an ASCII string\n"},
2863 {"asciilower", asciilower, METH_VARARGS, "lowercase an ASCII string\n"},
2823 {"asciiupper", asciiupper, METH_VARARGS, "uppercase an ASCII string\n"},
2864 {"asciiupper", asciiupper, METH_VARARGS, "uppercase an ASCII string\n"},
2824 {"dict_new_presized", dict_new_presized, METH_VARARGS,
2865 {"dict_new_presized", dict_new_presized, METH_VARARGS,
2825 "construct a dict with an expected size\n"},
2866 "construct a dict with an expected size\n"},
2826 {"make_file_foldmap", make_file_foldmap, METH_VARARGS,
2867 {"make_file_foldmap", make_file_foldmap, METH_VARARGS,
2827 "make file foldmap\n"},
2868 "make file foldmap\n"},
2828 {"encodedir", encodedir, METH_VARARGS, "encodedir a path\n"},
2869 {"encodedir", encodedir, METH_VARARGS, "encodedir a path\n"},
2829 {"pathencode", pathencode, METH_VARARGS, "fncache-encode a path\n"},
2870 {"pathencode", pathencode, METH_VARARGS, "fncache-encode a path\n"},
2830 {"lowerencode", lowerencode, METH_VARARGS, "lower-encode a path\n"},
2871 {"lowerencode", lowerencode, METH_VARARGS, "lower-encode a path\n"},
2831 {"fm1readmarkers", fm1readmarkers, METH_VARARGS,
2872 {"fm1readmarkers", fm1readmarkers, METH_VARARGS,
2832 "parse v1 obsolete markers\n"},
2873 "parse v1 obsolete markers\n"},
2833 {NULL, NULL}
2874 {NULL, NULL}
2834 };
2875 };
2835
2876
2836 void dirs_module_init(PyObject *mod);
2877 void dirs_module_init(PyObject *mod);
2837 void manifest_module_init(PyObject *mod);
2878 void manifest_module_init(PyObject *mod);
2838
2879
2839 static void module_init(PyObject *mod)
2880 static void module_init(PyObject *mod)
2840 {
2881 {
2841 /* This module constant has two purposes. First, it lets us unit test
2882 /* This module constant has two purposes. First, it lets us unit test
2842 * the ImportError raised without hard-coding any error text. This
2883 * the ImportError raised without hard-coding any error text. This
2843 * means we can change the text in the future without breaking tests,
2884 * means we can change the text in the future without breaking tests,
2844 * even across changesets without a recompile. Second, its presence
2885 * even across changesets without a recompile. Second, its presence
2845 * can be used to determine whether the version-checking logic is
2886 * can be used to determine whether the version-checking logic is
2846 * present, which also helps in testing across changesets without a
2887 * present, which also helps in testing across changesets without a
2847 * recompile. Note that this means the pure-Python version of parsers
2888 * recompile. Note that this means the pure-Python version of parsers
2848 * should not have this module constant. */
2889 * should not have this module constant. */
2849 PyModule_AddStringConstant(mod, "versionerrortext", versionerrortext);
2890 PyModule_AddStringConstant(mod, "versionerrortext", versionerrortext);
2850
2891
2851 dirs_module_init(mod);
2892 dirs_module_init(mod);
2852 manifest_module_init(mod);
2893 manifest_module_init(mod);
2853
2894
2854 indexType.tp_new = PyType_GenericNew;
2895 indexType.tp_new = PyType_GenericNew;
2855 if (PyType_Ready(&indexType) < 0 ||
2896 if (PyType_Ready(&indexType) < 0 ||
2856 PyType_Ready(&dirstateTupleType) < 0)
2897 PyType_Ready(&dirstateTupleType) < 0)
2857 return;
2898 return;
2858 Py_INCREF(&indexType);
2899 Py_INCREF(&indexType);
2859 PyModule_AddObject(mod, "index", (PyObject *)&indexType);
2900 PyModule_AddObject(mod, "index", (PyObject *)&indexType);
2860 Py_INCREF(&dirstateTupleType);
2901 Py_INCREF(&dirstateTupleType);
2861 PyModule_AddObject(mod, "dirstatetuple",
2902 PyModule_AddObject(mod, "dirstatetuple",
2862 (PyObject *)&dirstateTupleType);
2903 (PyObject *)&dirstateTupleType);
2863
2904
2864 nullentry = Py_BuildValue("iiiiiiis#", 0, 0, 0,
2905 nullentry = Py_BuildValue("iiiiiiis#", 0, 0, 0,
2865 -1, -1, -1, -1, nullid, 20);
2906 -1, -1, -1, -1, nullid, 20);
2866 if (nullentry)
2907 if (nullentry)
2867 PyObject_GC_UnTrack(nullentry);
2908 PyObject_GC_UnTrack(nullentry);
2868 }
2909 }
2869
2910
2870 static int check_python_version(void)
2911 static int check_python_version(void)
2871 {
2912 {
2872 PyObject *sys = PyImport_ImportModule("sys"), *ver;
2913 PyObject *sys = PyImport_ImportModule("sys"), *ver;
2873 long hexversion;
2914 long hexversion;
2874 if (!sys)
2915 if (!sys)
2875 return -1;
2916 return -1;
2876 ver = PyObject_GetAttrString(sys, "hexversion");
2917 ver = PyObject_GetAttrString(sys, "hexversion");
2877 Py_DECREF(sys);
2918 Py_DECREF(sys);
2878 if (!ver)
2919 if (!ver)
2879 return -1;
2920 return -1;
2880 hexversion = PyInt_AsLong(ver);
2921 hexversion = PyInt_AsLong(ver);
2881 Py_DECREF(ver);
2922 Py_DECREF(ver);
2882 /* sys.hexversion is a 32-bit number by default, so the -1 case
2923 /* sys.hexversion is a 32-bit number by default, so the -1 case
2883 * should only occur in unusual circumstances (e.g. if sys.hexversion
2924 * should only occur in unusual circumstances (e.g. if sys.hexversion
2884 * is manually set to an invalid value). */
2925 * is manually set to an invalid value). */
2885 if ((hexversion == -1) || (hexversion >> 16 != PY_VERSION_HEX >> 16)) {
2926 if ((hexversion == -1) || (hexversion >> 16 != PY_VERSION_HEX >> 16)) {
2886 PyErr_Format(PyExc_ImportError, "%s: The Mercurial extension "
2927 PyErr_Format(PyExc_ImportError, "%s: The Mercurial extension "
2887 "modules were compiled with Python " PY_VERSION ", but "
2928 "modules were compiled with Python " PY_VERSION ", but "
2888 "Mercurial is currently using Python with sys.hexversion=%ld: "
2929 "Mercurial is currently using Python with sys.hexversion=%ld: "
2889 "Python %s\n at: %s", versionerrortext, hexversion,
2930 "Python %s\n at: %s", versionerrortext, hexversion,
2890 Py_GetVersion(), Py_GetProgramFullPath());
2931 Py_GetVersion(), Py_GetProgramFullPath());
2891 return -1;
2932 return -1;
2892 }
2933 }
2893 return 0;
2934 return 0;
2894 }
2935 }
2895
2936
2896 #ifdef IS_PY3K
2937 #ifdef IS_PY3K
2897 static struct PyModuleDef parsers_module = {
2938 static struct PyModuleDef parsers_module = {
2898 PyModuleDef_HEAD_INIT,
2939 PyModuleDef_HEAD_INIT,
2899 "parsers",
2940 "parsers",
2900 parsers_doc,
2941 parsers_doc,
2901 -1,
2942 -1,
2902 methods
2943 methods
2903 };
2944 };
2904
2945
2905 PyMODINIT_FUNC PyInit_parsers(void)
2946 PyMODINIT_FUNC PyInit_parsers(void)
2906 {
2947 {
2907 PyObject *mod;
2948 PyObject *mod;
2908
2949
2909 if (check_python_version() == -1)
2950 if (check_python_version() == -1)
2910 return NULL;
2951 return NULL;
2911 mod = PyModule_Create(&parsers_module);
2952 mod = PyModule_Create(&parsers_module);
2912 module_init(mod);
2953 module_init(mod);
2913 return mod;
2954 return mod;
2914 }
2955 }
2915 #else
2956 #else
2916 PyMODINIT_FUNC initparsers(void)
2957 PyMODINIT_FUNC initparsers(void)
2917 {
2958 {
2918 PyObject *mod;
2959 PyObject *mod;
2919
2960
2920 if (check_python_version() == -1)
2961 if (check_python_version() == -1)
2921 return;
2962 return;
2922 mod = Py_InitModule3("parsers", methods, parsers_doc);
2963 mod = Py_InitModule3("parsers", methods, parsers_doc);
2923 module_init(mod);
2964 module_init(mod);
2924 }
2965 }
2925 #endif
2966 #endif
General Comments 0
You need to be logged in to leave comments. Login now