##// END OF EJS Templates
merge with stable
Matt Mackall -
r20169:507919a3 merge default
parent child Browse files
Show More
@@ -1,391 +1,393 b''
1 # hgweb/hgweb_mod.py - Web interface for a repository.
1 # hgweb/hgweb_mod.py - Web interface for a repository.
2 #
2 #
3 # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
3 # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
4 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
5 #
5 #
6 # This software may be used and distributed according to the terms of the
6 # This software may be used and distributed according to the terms of the
7 # GNU General Public License version 2 or any later version.
7 # GNU General Public License version 2 or any later version.
8
8
9 import os, re
9 import os, re
10 from mercurial import ui, hg, hook, error, encoding, templater, util, repoview
10 from mercurial import ui, hg, hook, error, encoding, templater, util, repoview
11 from mercurial.templatefilters import websub
11 from mercurial.templatefilters import websub
12 from mercurial.i18n import _
12 from mercurial.i18n import _
13 from common import get_stat, ErrorResponse, permhooks, caching
13 from common import get_stat, ErrorResponse, permhooks, caching
14 from common import HTTP_OK, HTTP_NOT_MODIFIED, HTTP_BAD_REQUEST
14 from common import HTTP_OK, HTTP_NOT_MODIFIED, HTTP_BAD_REQUEST
15 from common import HTTP_NOT_FOUND, HTTP_SERVER_ERROR
15 from common import HTTP_NOT_FOUND, HTTP_SERVER_ERROR
16 from request import wsgirequest
16 from request import wsgirequest
17 import webcommands, protocol, webutil
17 import webcommands, protocol, webutil
18
18
19 perms = {
19 perms = {
20 'changegroup': 'pull',
20 'changegroup': 'pull',
21 'changegroupsubset': 'pull',
21 'changegroupsubset': 'pull',
22 'getbundle': 'pull',
22 'getbundle': 'pull',
23 'stream_out': 'pull',
23 'stream_out': 'pull',
24 'listkeys': 'pull',
24 'listkeys': 'pull',
25 'unbundle': 'push',
25 'unbundle': 'push',
26 'pushkey': 'push',
26 'pushkey': 'push',
27 }
27 }
28
28
29 def makebreadcrumb(url, prefix=''):
29 def makebreadcrumb(url, prefix=''):
30 '''Return a 'URL breadcrumb' list
30 '''Return a 'URL breadcrumb' list
31
31
32 A 'URL breadcrumb' is a list of URL-name pairs,
32 A 'URL breadcrumb' is a list of URL-name pairs,
33 corresponding to each of the path items on a URL.
33 corresponding to each of the path items on a URL.
34 This can be used to create path navigation entries.
34 This can be used to create path navigation entries.
35 '''
35 '''
36 if url.endswith('/'):
36 if url.endswith('/'):
37 url = url[:-1]
37 url = url[:-1]
38 if prefix:
38 if prefix:
39 url = '/' + prefix + url
39 url = '/' + prefix + url
40 relpath = url
40 relpath = url
41 if relpath.startswith('/'):
41 if relpath.startswith('/'):
42 relpath = relpath[1:]
42 relpath = relpath[1:]
43
43
44 breadcrumb = []
44 breadcrumb = []
45 urlel = url
45 urlel = url
46 pathitems = [''] + relpath.split('/')
46 pathitems = [''] + relpath.split('/')
47 for pathel in reversed(pathitems):
47 for pathel in reversed(pathitems):
48 if not pathel or not urlel:
48 if not pathel or not urlel:
49 break
49 break
50 breadcrumb.append({'url': urlel, 'name': pathel})
50 breadcrumb.append({'url': urlel, 'name': pathel})
51 urlel = os.path.dirname(urlel)
51 urlel = os.path.dirname(urlel)
52 return reversed(breadcrumb)
52 return reversed(breadcrumb)
53
53
54
54
55 class hgweb(object):
55 class hgweb(object):
56 def __init__(self, repo, name=None, baseui=None):
56 def __init__(self, repo, name=None, baseui=None):
57 if isinstance(repo, str):
57 if isinstance(repo, str):
58 if baseui:
58 if baseui:
59 u = baseui.copy()
59 u = baseui.copy()
60 else:
60 else:
61 u = ui.ui()
61 u = ui.ui()
62 self.repo = hg.repository(u, repo)
62 r = hg.repository(u, repo)
63 else:
63 else:
64 self.repo = repo
64 r = repo
65
65
66 self.repo = self._getview(self.repo)
66 r = self._getview(r)
67 self.repo.ui.setconfig('ui', 'report_untrusted', 'off')
67 r.ui.setconfig('ui', 'report_untrusted', 'off')
68 self.repo.baseui.setconfig('ui', 'report_untrusted', 'off')
68 r.baseui.setconfig('ui', 'report_untrusted', 'off')
69 self.repo.ui.setconfig('ui', 'nontty', 'true')
69 r.ui.setconfig('ui', 'nontty', 'true')
70 self.repo.baseui.setconfig('ui', 'nontty', 'true')
70 r.baseui.setconfig('ui', 'nontty', 'true')
71 self.repo = r
71 hook.redirect(True)
72 hook.redirect(True)
72 self.mtime = -1
73 self.mtime = -1
73 self.size = -1
74 self.size = -1
74 self.reponame = name
75 self.reponame = name
75 self.archives = 'zip', 'gz', 'bz2'
76 self.archives = 'zip', 'gz', 'bz2'
76 self.stripecount = 1
77 self.stripecount = 1
77 # a repo owner may set web.templates in .hg/hgrc to get any file
78 # a repo owner may set web.templates in .hg/hgrc to get any file
78 # readable by the user running the CGI script
79 # readable by the user running the CGI script
79 self.templatepath = self.config('web', 'templates')
80 self.templatepath = self.config('web', 'templates')
80 self.websubtable = self.loadwebsub()
81 self.websubtable = self.loadwebsub()
81
82
82 # The CGI scripts are often run by a user different from the repo owner.
83 # The CGI scripts are often run by a user different from the repo owner.
83 # Trust the settings from the .hg/hgrc files by default.
84 # Trust the settings from the .hg/hgrc files by default.
84 def config(self, section, name, default=None, untrusted=True):
85 def config(self, section, name, default=None, untrusted=True):
85 return self.repo.ui.config(section, name, default,
86 return self.repo.ui.config(section, name, default,
86 untrusted=untrusted)
87 untrusted=untrusted)
87
88
88 def configbool(self, section, name, default=False, untrusted=True):
89 def configbool(self, section, name, default=False, untrusted=True):
89 return self.repo.ui.configbool(section, name, default,
90 return self.repo.ui.configbool(section, name, default,
90 untrusted=untrusted)
91 untrusted=untrusted)
91
92
92 def configlist(self, section, name, default=None, untrusted=True):
93 def configlist(self, section, name, default=None, untrusted=True):
93 return self.repo.ui.configlist(section, name, default,
94 return self.repo.ui.configlist(section, name, default,
94 untrusted=untrusted)
95 untrusted=untrusted)
95
96
96 def _getview(self, repo):
97 def _getview(self, repo):
97 viewconfig = self.config('web', 'view', 'served')
98 viewconfig = repo.ui.config('web', 'view', 'served',
99 untrusted=True)
98 if viewconfig == 'all':
100 if viewconfig == 'all':
99 return repo.unfiltered()
101 return repo.unfiltered()
100 elif viewconfig in repoview.filtertable:
102 elif viewconfig in repoview.filtertable:
101 return repo.filtered(viewconfig)
103 return repo.filtered(viewconfig)
102 else:
104 else:
103 return repo.filtered('served')
105 return repo.filtered('served')
104
106
105 def refresh(self, request=None):
107 def refresh(self, request=None):
106 st = get_stat(self.repo.spath)
108 st = get_stat(self.repo.spath)
107 # compare changelog size in addition to mtime to catch
109 # compare changelog size in addition to mtime to catch
108 # rollbacks made less than a second ago
110 # rollbacks made less than a second ago
109 if st.st_mtime != self.mtime or st.st_size != self.size:
111 if st.st_mtime != self.mtime or st.st_size != self.size:
110 self.mtime = st.st_mtime
112 self.mtime = st.st_mtime
111 self.size = st.st_size
113 self.size = st.st_size
112 r = hg.repository(self.repo.baseui, self.repo.root)
114 r = hg.repository(self.repo.baseui, self.repo.root)
113 self.repo = self._getview(r)
115 self.repo = self._getview(r)
114 self.maxchanges = int(self.config("web", "maxchanges", 10))
116 self.maxchanges = int(self.config("web", "maxchanges", 10))
115 self.stripecount = int(self.config("web", "stripes", 1))
117 self.stripecount = int(self.config("web", "stripes", 1))
116 self.maxshortchanges = int(self.config("web", "maxshortchanges",
118 self.maxshortchanges = int(self.config("web", "maxshortchanges",
117 60))
119 60))
118 self.maxfiles = int(self.config("web", "maxfiles", 10))
120 self.maxfiles = int(self.config("web", "maxfiles", 10))
119 self.allowpull = self.configbool("web", "allowpull", True)
121 self.allowpull = self.configbool("web", "allowpull", True)
120 encoding.encoding = self.config("web", "encoding",
122 encoding.encoding = self.config("web", "encoding",
121 encoding.encoding)
123 encoding.encoding)
122 if request:
124 if request:
123 self.repo.ui.environ = request.env
125 self.repo.ui.environ = request.env
124
126
125 def run(self):
127 def run(self):
126 if not os.environ.get('GATEWAY_INTERFACE', '').startswith("CGI/1."):
128 if not os.environ.get('GATEWAY_INTERFACE', '').startswith("CGI/1."):
127 raise RuntimeError("This function is only intended to be "
129 raise RuntimeError("This function is only intended to be "
128 "called while running as a CGI script.")
130 "called while running as a CGI script.")
129 import mercurial.hgweb.wsgicgi as wsgicgi
131 import mercurial.hgweb.wsgicgi as wsgicgi
130 wsgicgi.launch(self)
132 wsgicgi.launch(self)
131
133
132 def __call__(self, env, respond):
134 def __call__(self, env, respond):
133 req = wsgirequest(env, respond)
135 req = wsgirequest(env, respond)
134 return self.run_wsgi(req)
136 return self.run_wsgi(req)
135
137
136 def run_wsgi(self, req):
138 def run_wsgi(self, req):
137
139
138 self.refresh(req)
140 self.refresh(req)
139
141
140 # work with CGI variables to create coherent structure
142 # work with CGI variables to create coherent structure
141 # use SCRIPT_NAME, PATH_INFO and QUERY_STRING as well as our REPO_NAME
143 # use SCRIPT_NAME, PATH_INFO and QUERY_STRING as well as our REPO_NAME
142
144
143 req.url = req.env['SCRIPT_NAME']
145 req.url = req.env['SCRIPT_NAME']
144 if not req.url.endswith('/'):
146 if not req.url.endswith('/'):
145 req.url += '/'
147 req.url += '/'
146 if 'REPO_NAME' in req.env:
148 if 'REPO_NAME' in req.env:
147 req.url += req.env['REPO_NAME'] + '/'
149 req.url += req.env['REPO_NAME'] + '/'
148
150
149 if 'PATH_INFO' in req.env:
151 if 'PATH_INFO' in req.env:
150 parts = req.env['PATH_INFO'].strip('/').split('/')
152 parts = req.env['PATH_INFO'].strip('/').split('/')
151 repo_parts = req.env.get('REPO_NAME', '').split('/')
153 repo_parts = req.env.get('REPO_NAME', '').split('/')
152 if parts[:len(repo_parts)] == repo_parts:
154 if parts[:len(repo_parts)] == repo_parts:
153 parts = parts[len(repo_parts):]
155 parts = parts[len(repo_parts):]
154 query = '/'.join(parts)
156 query = '/'.join(parts)
155 else:
157 else:
156 query = req.env['QUERY_STRING'].split('&', 1)[0]
158 query = req.env['QUERY_STRING'].split('&', 1)[0]
157 query = query.split(';', 1)[0]
159 query = query.split(';', 1)[0]
158
160
159 # process this if it's a protocol request
161 # process this if it's a protocol request
160 # protocol bits don't need to create any URLs
162 # protocol bits don't need to create any URLs
161 # and the clients always use the old URL structure
163 # and the clients always use the old URL structure
162
164
163 cmd = req.form.get('cmd', [''])[0]
165 cmd = req.form.get('cmd', [''])[0]
164 if protocol.iscmd(cmd):
166 if protocol.iscmd(cmd):
165 try:
167 try:
166 if query:
168 if query:
167 raise ErrorResponse(HTTP_NOT_FOUND)
169 raise ErrorResponse(HTTP_NOT_FOUND)
168 if cmd in perms:
170 if cmd in perms:
169 self.check_perm(req, perms[cmd])
171 self.check_perm(req, perms[cmd])
170 return protocol.call(self.repo, req, cmd)
172 return protocol.call(self.repo, req, cmd)
171 except ErrorResponse, inst:
173 except ErrorResponse, inst:
172 # A client that sends unbundle without 100-continue will
174 # A client that sends unbundle without 100-continue will
173 # break if we respond early.
175 # break if we respond early.
174 if (cmd == 'unbundle' and
176 if (cmd == 'unbundle' and
175 (req.env.get('HTTP_EXPECT',
177 (req.env.get('HTTP_EXPECT',
176 '').lower() != '100-continue') or
178 '').lower() != '100-continue') or
177 req.env.get('X-HgHttp2', '')):
179 req.env.get('X-HgHttp2', '')):
178 req.drain()
180 req.drain()
179 else:
181 else:
180 req.headers.append(('Connection', 'Close'))
182 req.headers.append(('Connection', 'Close'))
181 req.respond(inst, protocol.HGTYPE,
183 req.respond(inst, protocol.HGTYPE,
182 body='0\n%s\n' % inst.message)
184 body='0\n%s\n' % inst.message)
183 return ''
185 return ''
184
186
185 # translate user-visible url structure to internal structure
187 # translate user-visible url structure to internal structure
186
188
187 args = query.split('/', 2)
189 args = query.split('/', 2)
188 if 'cmd' not in req.form and args and args[0]:
190 if 'cmd' not in req.form and args and args[0]:
189
191
190 cmd = args.pop(0)
192 cmd = args.pop(0)
191 style = cmd.rfind('-')
193 style = cmd.rfind('-')
192 if style != -1:
194 if style != -1:
193 req.form['style'] = [cmd[:style]]
195 req.form['style'] = [cmd[:style]]
194 cmd = cmd[style + 1:]
196 cmd = cmd[style + 1:]
195
197
196 # avoid accepting e.g. style parameter as command
198 # avoid accepting e.g. style parameter as command
197 if util.safehasattr(webcommands, cmd):
199 if util.safehasattr(webcommands, cmd):
198 req.form['cmd'] = [cmd]
200 req.form['cmd'] = [cmd]
199 else:
201 else:
200 cmd = ''
202 cmd = ''
201
203
202 if cmd == 'static':
204 if cmd == 'static':
203 req.form['file'] = ['/'.join(args)]
205 req.form['file'] = ['/'.join(args)]
204 else:
206 else:
205 if args and args[0]:
207 if args and args[0]:
206 node = args.pop(0)
208 node = args.pop(0)
207 req.form['node'] = [node]
209 req.form['node'] = [node]
208 if args:
210 if args:
209 req.form['file'] = args
211 req.form['file'] = args
210
212
211 ua = req.env.get('HTTP_USER_AGENT', '')
213 ua = req.env.get('HTTP_USER_AGENT', '')
212 if cmd == 'rev' and 'mercurial' in ua:
214 if cmd == 'rev' and 'mercurial' in ua:
213 req.form['style'] = ['raw']
215 req.form['style'] = ['raw']
214
216
215 if cmd == 'archive':
217 if cmd == 'archive':
216 fn = req.form['node'][0]
218 fn = req.form['node'][0]
217 for type_, spec in self.archive_specs.iteritems():
219 for type_, spec in self.archive_specs.iteritems():
218 ext = spec[2]
220 ext = spec[2]
219 if fn.endswith(ext):
221 if fn.endswith(ext):
220 req.form['node'] = [fn[:-len(ext)]]
222 req.form['node'] = [fn[:-len(ext)]]
221 req.form['type'] = [type_]
223 req.form['type'] = [type_]
222
224
223 # process the web interface request
225 # process the web interface request
224
226
225 try:
227 try:
226 tmpl = self.templater(req)
228 tmpl = self.templater(req)
227 ctype = tmpl('mimetype', encoding=encoding.encoding)
229 ctype = tmpl('mimetype', encoding=encoding.encoding)
228 ctype = templater.stringify(ctype)
230 ctype = templater.stringify(ctype)
229
231
230 # check read permissions non-static content
232 # check read permissions non-static content
231 if cmd != 'static':
233 if cmd != 'static':
232 self.check_perm(req, None)
234 self.check_perm(req, None)
233
235
234 if cmd == '':
236 if cmd == '':
235 req.form['cmd'] = [tmpl.cache['default']]
237 req.form['cmd'] = [tmpl.cache['default']]
236 cmd = req.form['cmd'][0]
238 cmd = req.form['cmd'][0]
237
239
238 if self.configbool('web', 'cache', True):
240 if self.configbool('web', 'cache', True):
239 caching(self, req) # sets ETag header or raises NOT_MODIFIED
241 caching(self, req) # sets ETag header or raises NOT_MODIFIED
240 if cmd not in webcommands.__all__:
242 if cmd not in webcommands.__all__:
241 msg = 'no such method: %s' % cmd
243 msg = 'no such method: %s' % cmd
242 raise ErrorResponse(HTTP_BAD_REQUEST, msg)
244 raise ErrorResponse(HTTP_BAD_REQUEST, msg)
243 elif cmd == 'file' and 'raw' in req.form.get('style', []):
245 elif cmd == 'file' and 'raw' in req.form.get('style', []):
244 self.ctype = ctype
246 self.ctype = ctype
245 content = webcommands.rawfile(self, req, tmpl)
247 content = webcommands.rawfile(self, req, tmpl)
246 else:
248 else:
247 content = getattr(webcommands, cmd)(self, req, tmpl)
249 content = getattr(webcommands, cmd)(self, req, tmpl)
248 req.respond(HTTP_OK, ctype)
250 req.respond(HTTP_OK, ctype)
249
251
250 return content
252 return content
251
253
252 except (error.LookupError, error.RepoLookupError), err:
254 except (error.LookupError, error.RepoLookupError), err:
253 req.respond(HTTP_NOT_FOUND, ctype)
255 req.respond(HTTP_NOT_FOUND, ctype)
254 msg = str(err)
256 msg = str(err)
255 if (util.safehasattr(err, 'name') and
257 if (util.safehasattr(err, 'name') and
256 not isinstance(err, error.ManifestLookupError)):
258 not isinstance(err, error.ManifestLookupError)):
257 msg = 'revision not found: %s' % err.name
259 msg = 'revision not found: %s' % err.name
258 return tmpl('error', error=msg)
260 return tmpl('error', error=msg)
259 except (error.RepoError, error.RevlogError), inst:
261 except (error.RepoError, error.RevlogError), inst:
260 req.respond(HTTP_SERVER_ERROR, ctype)
262 req.respond(HTTP_SERVER_ERROR, ctype)
261 return tmpl('error', error=str(inst))
263 return tmpl('error', error=str(inst))
262 except ErrorResponse, inst:
264 except ErrorResponse, inst:
263 req.respond(inst, ctype)
265 req.respond(inst, ctype)
264 if inst.code == HTTP_NOT_MODIFIED:
266 if inst.code == HTTP_NOT_MODIFIED:
265 # Not allowed to return a body on a 304
267 # Not allowed to return a body on a 304
266 return ['']
268 return ['']
267 return tmpl('error', error=inst.message)
269 return tmpl('error', error=inst.message)
268
270
269 def loadwebsub(self):
271 def loadwebsub(self):
270 websubtable = []
272 websubtable = []
271 websubdefs = self.repo.ui.configitems('websub')
273 websubdefs = self.repo.ui.configitems('websub')
272 # we must maintain interhg backwards compatibility
274 # we must maintain interhg backwards compatibility
273 websubdefs += self.repo.ui.configitems('interhg')
275 websubdefs += self.repo.ui.configitems('interhg')
274 for key, pattern in websubdefs:
276 for key, pattern in websubdefs:
275 # grab the delimiter from the character after the "s"
277 # grab the delimiter from the character after the "s"
276 unesc = pattern[1]
278 unesc = pattern[1]
277 delim = re.escape(unesc)
279 delim = re.escape(unesc)
278
280
279 # identify portions of the pattern, taking care to avoid escaped
281 # identify portions of the pattern, taking care to avoid escaped
280 # delimiters. the replace format and flags are optional, but
282 # delimiters. the replace format and flags are optional, but
281 # delimiters are required.
283 # delimiters are required.
282 match = re.match(
284 match = re.match(
283 r'^s%s(.+)(?:(?<=\\\\)|(?<!\\))%s(.*)%s([ilmsux])*$'
285 r'^s%s(.+)(?:(?<=\\\\)|(?<!\\))%s(.*)%s([ilmsux])*$'
284 % (delim, delim, delim), pattern)
286 % (delim, delim, delim), pattern)
285 if not match:
287 if not match:
286 self.repo.ui.warn(_("websub: invalid pattern for %s: %s\n")
288 self.repo.ui.warn(_("websub: invalid pattern for %s: %s\n")
287 % (key, pattern))
289 % (key, pattern))
288 continue
290 continue
289
291
290 # we need to unescape the delimiter for regexp and format
292 # we need to unescape the delimiter for regexp and format
291 delim_re = re.compile(r'(?<!\\)\\%s' % delim)
293 delim_re = re.compile(r'(?<!\\)\\%s' % delim)
292 regexp = delim_re.sub(unesc, match.group(1))
294 regexp = delim_re.sub(unesc, match.group(1))
293 format = delim_re.sub(unesc, match.group(2))
295 format = delim_re.sub(unesc, match.group(2))
294
296
295 # the pattern allows for 6 regexp flags, so set them if necessary
297 # the pattern allows for 6 regexp flags, so set them if necessary
296 flagin = match.group(3)
298 flagin = match.group(3)
297 flags = 0
299 flags = 0
298 if flagin:
300 if flagin:
299 for flag in flagin.upper():
301 for flag in flagin.upper():
300 flags |= re.__dict__[flag]
302 flags |= re.__dict__[flag]
301
303
302 try:
304 try:
303 regexp = re.compile(regexp, flags)
305 regexp = re.compile(regexp, flags)
304 websubtable.append((regexp, format))
306 websubtable.append((regexp, format))
305 except re.error:
307 except re.error:
306 self.repo.ui.warn(_("websub: invalid regexp for %s: %s\n")
308 self.repo.ui.warn(_("websub: invalid regexp for %s: %s\n")
307 % (key, regexp))
309 % (key, regexp))
308 return websubtable
310 return websubtable
309
311
310 def templater(self, req):
312 def templater(self, req):
311
313
312 # determine scheme, port and server name
314 # determine scheme, port and server name
313 # this is needed to create absolute urls
315 # this is needed to create absolute urls
314
316
315 proto = req.env.get('wsgi.url_scheme')
317 proto = req.env.get('wsgi.url_scheme')
316 if proto == 'https':
318 if proto == 'https':
317 proto = 'https'
319 proto = 'https'
318 default_port = "443"
320 default_port = "443"
319 else:
321 else:
320 proto = 'http'
322 proto = 'http'
321 default_port = "80"
323 default_port = "80"
322
324
323 port = req.env["SERVER_PORT"]
325 port = req.env["SERVER_PORT"]
324 port = port != default_port and (":" + port) or ""
326 port = port != default_port and (":" + port) or ""
325 urlbase = '%s://%s%s' % (proto, req.env['SERVER_NAME'], port)
327 urlbase = '%s://%s%s' % (proto, req.env['SERVER_NAME'], port)
326 logourl = self.config("web", "logourl", "http://mercurial.selenic.com/")
328 logourl = self.config("web", "logourl", "http://mercurial.selenic.com/")
327 logoimg = self.config("web", "logoimg", "hglogo.png")
329 logoimg = self.config("web", "logoimg", "hglogo.png")
328 staticurl = self.config("web", "staticurl") or req.url + 'static/'
330 staticurl = self.config("web", "staticurl") or req.url + 'static/'
329 if not staticurl.endswith('/'):
331 if not staticurl.endswith('/'):
330 staticurl += '/'
332 staticurl += '/'
331
333
332 # some functions for the templater
334 # some functions for the templater
333
335
334 def motd(**map):
336 def motd(**map):
335 yield self.config("web", "motd", "")
337 yield self.config("web", "motd", "")
336
338
337 # figure out which style to use
339 # figure out which style to use
338
340
339 vars = {}
341 vars = {}
340 styles = (
342 styles = (
341 req.form.get('style', [None])[0],
343 req.form.get('style', [None])[0],
342 self.config('web', 'style'),
344 self.config('web', 'style'),
343 'paper',
345 'paper',
344 )
346 )
345 style, mapfile = templater.stylemap(styles, self.templatepath)
347 style, mapfile = templater.stylemap(styles, self.templatepath)
346 if style == styles[0]:
348 if style == styles[0]:
347 vars['style'] = style
349 vars['style'] = style
348
350
349 start = req.url[-1] == '?' and '&' or '?'
351 start = req.url[-1] == '?' and '&' or '?'
350 sessionvars = webutil.sessionvars(vars, start)
352 sessionvars = webutil.sessionvars(vars, start)
351
353
352 if not self.reponame:
354 if not self.reponame:
353 self.reponame = (self.config("web", "name")
355 self.reponame = (self.config("web", "name")
354 or req.env.get('REPO_NAME')
356 or req.env.get('REPO_NAME')
355 or req.url.strip('/') or self.repo.root)
357 or req.url.strip('/') or self.repo.root)
356
358
357 def websubfilter(text):
359 def websubfilter(text):
358 return websub(text, self.websubtable)
360 return websub(text, self.websubtable)
359
361
360 # create the templater
362 # create the templater
361
363
362 tmpl = templater.templater(mapfile,
364 tmpl = templater.templater(mapfile,
363 filters={"websub": websubfilter},
365 filters={"websub": websubfilter},
364 defaults={"url": req.url,
366 defaults={"url": req.url,
365 "logourl": logourl,
367 "logourl": logourl,
366 "logoimg": logoimg,
368 "logoimg": logoimg,
367 "staticurl": staticurl,
369 "staticurl": staticurl,
368 "urlbase": urlbase,
370 "urlbase": urlbase,
369 "repo": self.reponame,
371 "repo": self.reponame,
370 "encoding": encoding.encoding,
372 "encoding": encoding.encoding,
371 "motd": motd,
373 "motd": motd,
372 "sessionvars": sessionvars,
374 "sessionvars": sessionvars,
373 "pathdef": makebreadcrumb(req.url),
375 "pathdef": makebreadcrumb(req.url),
374 })
376 })
375 return tmpl
377 return tmpl
376
378
377 def archivelist(self, nodeid):
379 def archivelist(self, nodeid):
378 allowed = self.configlist("web", "allow_archive")
380 allowed = self.configlist("web", "allow_archive")
379 for i, spec in self.archive_specs.iteritems():
381 for i, spec in self.archive_specs.iteritems():
380 if i in allowed or self.configbool("web", "allow" + i):
382 if i in allowed or self.configbool("web", "allow" + i):
381 yield {"type" : i, "extension" : spec[2], "node" : nodeid}
383 yield {"type" : i, "extension" : spec[2], "node" : nodeid}
382
384
383 archive_specs = {
385 archive_specs = {
384 'bz2': ('application/x-bzip2', 'tbz2', '.tar.bz2', None),
386 'bz2': ('application/x-bzip2', 'tbz2', '.tar.bz2', None),
385 'gz': ('application/x-gzip', 'tgz', '.tar.gz', None),
387 'gz': ('application/x-gzip', 'tgz', '.tar.gz', None),
386 'zip': ('application/zip', 'zip', '.zip', None),
388 'zip': ('application/zip', 'zip', '.zip', None),
387 }
389 }
388
390
389 def check_perm(self, req, op):
391 def check_perm(self, req, op):
390 for hook in permhooks:
392 for hook in permhooks:
391 hook(self, req, op)
393 hook(self, req, op)
@@ -1,429 +1,420 b''
1 /*
1 /*
2 mpatch.c - efficient binary patching for Mercurial
2 mpatch.c - efficient binary patching for Mercurial
3
3
4 This implements a patch algorithm that's O(m + nlog n) where m is the
4 This implements a patch algorithm that's O(m + nlog n) where m is the
5 size of the output and n is the number of patches.
5 size of the output and n is the number of patches.
6
6
7 Given a list of binary patches, it unpacks each into a hunk list,
7 Given a list of binary patches, it unpacks each into a hunk list,
8 then combines the hunk lists with a treewise recursion to form a
8 then combines the hunk lists with a treewise recursion to form a
9 single hunk list. This hunk list is then applied to the original
9 single hunk list. This hunk list is then applied to the original
10 text.
10 text.
11
11
12 The text (or binary) fragments are copied directly from their source
12 The text (or binary) fragments are copied directly from their source
13 Python objects into a preallocated output string to avoid the
13 Python objects into a preallocated output string to avoid the
14 allocation of intermediate Python objects. Working memory is about 2x
14 allocation of intermediate Python objects. Working memory is about 2x
15 the total number of hunks.
15 the total number of hunks.
16
16
17 Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
17 Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
18
18
19 This software may be used and distributed according to the terms
19 This software may be used and distributed according to the terms
20 of the GNU General Public License, incorporated herein by reference.
20 of the GNU General Public License, incorporated herein by reference.
21 */
21 */
22
22
23 #define PY_SSIZE_T_CLEAN
23 #define PY_SSIZE_T_CLEAN
24 #include <Python.h>
24 #include <Python.h>
25 #include <stdlib.h>
25 #include <stdlib.h>
26 #include <string.h>
26 #include <string.h>
27
27
28 #include "util.h"
28 #include "util.h"
29
29
30 static char mpatch_doc[] = "Efficient binary patching.";
30 static char mpatch_doc[] = "Efficient binary patching.";
31 static PyObject *mpatch_Error;
31 static PyObject *mpatch_Error;
32
32
33 struct frag {
33 struct frag {
34 int start, end, len;
34 int start, end, len;
35 const char *data;
35 const char *data;
36 };
36 };
37
37
38 struct flist {
38 struct flist {
39 struct frag *base, *head, *tail;
39 struct frag *base, *head, *tail;
40 };
40 };
41
41
42 static struct flist *lalloc(Py_ssize_t size)
42 static struct flist *lalloc(Py_ssize_t size)
43 {
43 {
44 struct flist *a = NULL;
44 struct flist *a = NULL;
45
45
46 if (size < 1)
46 if (size < 1)
47 size = 1;
47 size = 1;
48
48
49 a = (struct flist *)malloc(sizeof(struct flist));
49 a = (struct flist *)malloc(sizeof(struct flist));
50 if (a) {
50 if (a) {
51 a->base = (struct frag *)malloc(sizeof(struct frag) * size);
51 a->base = (struct frag *)malloc(sizeof(struct frag) * size);
52 if (a->base) {
52 if (a->base) {
53 a->head = a->tail = a->base;
53 a->head = a->tail = a->base;
54 return a;
54 return a;
55 }
55 }
56 free(a);
56 free(a);
57 a = NULL;
57 a = NULL;
58 }
58 }
59 if (!PyErr_Occurred())
59 if (!PyErr_Occurred())
60 PyErr_NoMemory();
60 PyErr_NoMemory();
61 return NULL;
61 return NULL;
62 }
62 }
63
63
64 static void lfree(struct flist *a)
64 static void lfree(struct flist *a)
65 {
65 {
66 if (a) {
66 if (a) {
67 free(a->base);
67 free(a->base);
68 free(a);
68 free(a);
69 }
69 }
70 }
70 }
71
71
72 static Py_ssize_t lsize(struct flist *a)
72 static Py_ssize_t lsize(struct flist *a)
73 {
73 {
74 return a->tail - a->head;
74 return a->tail - a->head;
75 }
75 }
76
76
77 /* move hunks in source that are less cut to dest, compensating
77 /* move hunks in source that are less cut to dest, compensating
78 for changes in offset. the last hunk may be split if necessary.
78 for changes in offset. the last hunk may be split if necessary.
79 */
79 */
80 static int gather(struct flist *dest, struct flist *src, int cut, int offset)
80 static int gather(struct flist *dest, struct flist *src, int cut, int offset)
81 {
81 {
82 struct frag *d = dest->tail, *s = src->head;
82 struct frag *d = dest->tail, *s = src->head;
83 int postend, c, l;
83 int postend, c, l;
84
84
85 while (s != src->tail) {
85 while (s != src->tail) {
86 if (s->start + offset >= cut)
86 if (s->start + offset >= cut)
87 break; /* we've gone far enough */
87 break; /* we've gone far enough */
88
88
89 postend = offset + s->start + s->len;
89 postend = offset + s->start + s->len;
90 if (postend <= cut) {
90 if (postend <= cut) {
91 /* save this hunk */
91 /* save this hunk */
92 offset += s->start + s->len - s->end;
92 offset += s->start + s->len - s->end;
93 *d++ = *s++;
93 *d++ = *s++;
94 }
94 }
95 else {
95 else {
96 /* break up this hunk */
96 /* break up this hunk */
97 c = cut - offset;
97 c = cut - offset;
98 if (s->end < c)
98 if (s->end < c)
99 c = s->end;
99 c = s->end;
100 l = cut - offset - s->start;
100 l = cut - offset - s->start;
101 if (s->len < l)
101 if (s->len < l)
102 l = s->len;
102 l = s->len;
103
103
104 offset += s->start + l - c;
104 offset += s->start + l - c;
105
105
106 d->start = s->start;
106 d->start = s->start;
107 d->end = c;
107 d->end = c;
108 d->len = l;
108 d->len = l;
109 d->data = s->data;
109 d->data = s->data;
110 d++;
110 d++;
111 s->start = c;
111 s->start = c;
112 s->len = s->len - l;
112 s->len = s->len - l;
113 s->data = s->data + l;
113 s->data = s->data + l;
114
114
115 break;
115 break;
116 }
116 }
117 }
117 }
118
118
119 dest->tail = d;
119 dest->tail = d;
120 src->head = s;
120 src->head = s;
121 return offset;
121 return offset;
122 }
122 }
123
123
124 /* like gather, but with no output list */
124 /* like gather, but with no output list */
125 static int discard(struct flist *src, int cut, int offset)
125 static int discard(struct flist *src, int cut, int offset)
126 {
126 {
127 struct frag *s = src->head;
127 struct frag *s = src->head;
128 int postend, c, l;
128 int postend, c, l;
129
129
130 while (s != src->tail) {
130 while (s != src->tail) {
131 if (s->start + offset >= cut)
131 if (s->start + offset >= cut)
132 break;
132 break;
133
133
134 postend = offset + s->start + s->len;
134 postend = offset + s->start + s->len;
135 if (postend <= cut) {
135 if (postend <= cut) {
136 offset += s->start + s->len - s->end;
136 offset += s->start + s->len - s->end;
137 s++;
137 s++;
138 }
138 }
139 else {
139 else {
140 c = cut - offset;
140 c = cut - offset;
141 if (s->end < c)
141 if (s->end < c)
142 c = s->end;
142 c = s->end;
143 l = cut - offset - s->start;
143 l = cut - offset - s->start;
144 if (s->len < l)
144 if (s->len < l)
145 l = s->len;
145 l = s->len;
146
146
147 offset += s->start + l - c;
147 offset += s->start + l - c;
148 s->start = c;
148 s->start = c;
149 s->len = s->len - l;
149 s->len = s->len - l;
150 s->data = s->data + l;
150 s->data = s->data + l;
151
151
152 break;
152 break;
153 }
153 }
154 }
154 }
155
155
156 src->head = s;
156 src->head = s;
157 return offset;
157 return offset;
158 }
158 }
159
159
160 /* combine hunk lists a and b, while adjusting b for offset changes in a/
160 /* combine hunk lists a and b, while adjusting b for offset changes in a/
161 this deletes a and b and returns the resultant list. */
161 this deletes a and b and returns the resultant list. */
162 static struct flist *combine(struct flist *a, struct flist *b)
162 static struct flist *combine(struct flist *a, struct flist *b)
163 {
163 {
164 struct flist *c = NULL;
164 struct flist *c = NULL;
165 struct frag *bh, *ct;
165 struct frag *bh, *ct;
166 int offset = 0, post;
166 int offset = 0, post;
167
167
168 if (a && b)
168 if (a && b)
169 c = lalloc((lsize(a) + lsize(b)) * 2);
169 c = lalloc((lsize(a) + lsize(b)) * 2);
170
170
171 if (c) {
171 if (c) {
172
172
173 for (bh = b->head; bh != b->tail; bh++) {
173 for (bh = b->head; bh != b->tail; bh++) {
174 /* save old hunks */
174 /* save old hunks */
175 offset = gather(c, a, bh->start, offset);
175 offset = gather(c, a, bh->start, offset);
176
176
177 /* discard replaced hunks */
177 /* discard replaced hunks */
178 post = discard(a, bh->end, offset);
178 post = discard(a, bh->end, offset);
179
179
180 /* insert new hunk */
180 /* insert new hunk */
181 ct = c->tail;
181 ct = c->tail;
182 ct->start = bh->start - offset;
182 ct->start = bh->start - offset;
183 ct->end = bh->end - post;
183 ct->end = bh->end - post;
184 ct->len = bh->len;
184 ct->len = bh->len;
185 ct->data = bh->data;
185 ct->data = bh->data;
186 c->tail++;
186 c->tail++;
187 offset = post;
187 offset = post;
188 }
188 }
189
189
190 /* hold on to tail from a */
190 /* hold on to tail from a */
191 memcpy(c->tail, a->head, sizeof(struct frag) * lsize(a));
191 memcpy(c->tail, a->head, sizeof(struct frag) * lsize(a));
192 c->tail += lsize(a);
192 c->tail += lsize(a);
193 }
193 }
194
194
195 lfree(a);
195 lfree(a);
196 lfree(b);
196 lfree(b);
197 return c;
197 return c;
198 }
198 }
199
199
200 /* decode a binary patch into a hunk list */
200 /* decode a binary patch into a hunk list */
201 static struct flist *decode(const char *bin, Py_ssize_t len)
201 static struct flist *decode(const char *bin, Py_ssize_t len)
202 {
202 {
203 struct flist *l;
203 struct flist *l;
204 struct frag *lt;
204 struct frag *lt;
205 const char *data = bin + 12, *end = bin + len;
205 int pos = 0;
206
206
207 /* assume worst case size, we won't have many of these lists */
207 /* assume worst case size, we won't have many of these lists */
208 l = lalloc(len / 12);
208 l = lalloc(len / 12);
209 if (!l)
209 if (!l)
210 return NULL;
210 return NULL;
211
211
212 lt = l->tail;
212 lt = l->tail;
213
213
214 while (data <= end) {
214 while (pos >= 0 && pos < len) {
215 lt->start = getbe32(bin);
215 lt->start = getbe32(bin + pos);
216 lt->end = getbe32(bin + 4);
216 lt->end = getbe32(bin + pos + 4);
217 lt->len = getbe32(bin + 8);
217 lt->len = getbe32(bin + pos + 8);
218 if (lt->start > lt->end)
218 if (lt->start > lt->end)
219 break; /* sanity check */
219 break; /* sanity check */
220 bin = data + lt->len;
220 lt->data = bin + pos + 12;
221 if (bin < data)
221 pos += 12 + lt->len;
222 break; /* big data + big (bogus) len can wrap around */
223 lt->data = data;
224 data = bin + 12;
225 lt++;
222 lt++;
226 }
223 }
227
224
228 if (bin != end) {
225 if (pos != len) {
229 if (!PyErr_Occurred())
226 if (!PyErr_Occurred())
230 PyErr_SetString(mpatch_Error, "patch cannot be decoded");
227 PyErr_SetString(mpatch_Error, "patch cannot be decoded");
231 lfree(l);
228 lfree(l);
232 return NULL;
229 return NULL;
233 }
230 }
234
231
235 l->tail = lt;
232 l->tail = lt;
236 return l;
233 return l;
237 }
234 }
238
235
239 /* calculate the size of resultant text */
236 /* calculate the size of resultant text */
240 static Py_ssize_t calcsize(Py_ssize_t len, struct flist *l)
237 static Py_ssize_t calcsize(Py_ssize_t len, struct flist *l)
241 {
238 {
242 Py_ssize_t outlen = 0, last = 0;
239 Py_ssize_t outlen = 0, last = 0;
243 struct frag *f = l->head;
240 struct frag *f = l->head;
244
241
245 while (f != l->tail) {
242 while (f != l->tail) {
246 if (f->start < last || f->end > len) {
243 if (f->start < last || f->end > len) {
247 if (!PyErr_Occurred())
244 if (!PyErr_Occurred())
248 PyErr_SetString(mpatch_Error,
245 PyErr_SetString(mpatch_Error,
249 "invalid patch");
246 "invalid patch");
250 return -1;
247 return -1;
251 }
248 }
252 outlen += f->start - last;
249 outlen += f->start - last;
253 last = f->end;
250 last = f->end;
254 outlen += f->len;
251 outlen += f->len;
255 f++;
252 f++;
256 }
253 }
257
254
258 outlen += len - last;
255 outlen += len - last;
259 return outlen;
256 return outlen;
260 }
257 }
261
258
262 static int apply(char *buf, const char *orig, Py_ssize_t len, struct flist *l)
259 static int apply(char *buf, const char *orig, Py_ssize_t len, struct flist *l)
263 {
260 {
264 struct frag *f = l->head;
261 struct frag *f = l->head;
265 int last = 0;
262 int last = 0;
266 char *p = buf;
263 char *p = buf;
267
264
268 while (f != l->tail) {
265 while (f != l->tail) {
269 if (f->start < last || f->end > len) {
266 if (f->start < last || f->end > len) {
270 if (!PyErr_Occurred())
267 if (!PyErr_Occurred())
271 PyErr_SetString(mpatch_Error,
268 PyErr_SetString(mpatch_Error,
272 "invalid patch");
269 "invalid patch");
273 return 0;
270 return 0;
274 }
271 }
275 memcpy(p, orig + last, f->start - last);
272 memcpy(p, orig + last, f->start - last);
276 p += f->start - last;
273 p += f->start - last;
277 memcpy(p, f->data, f->len);
274 memcpy(p, f->data, f->len);
278 last = f->end;
275 last = f->end;
279 p += f->len;
276 p += f->len;
280 f++;
277 f++;
281 }
278 }
282 memcpy(p, orig + last, len - last);
279 memcpy(p, orig + last, len - last);
283 return 1;
280 return 1;
284 }
281 }
285
282
286 /* recursively generate a patch of all bins between start and end */
283 /* recursively generate a patch of all bins between start and end */
287 static struct flist *fold(PyObject *bins, Py_ssize_t start, Py_ssize_t end)
284 static struct flist *fold(PyObject *bins, Py_ssize_t start, Py_ssize_t end)
288 {
285 {
289 Py_ssize_t len, blen;
286 Py_ssize_t len, blen;
290 const char *buffer;
287 const char *buffer;
291
288
292 if (start + 1 == end) {
289 if (start + 1 == end) {
293 /* trivial case, output a decoded list */
290 /* trivial case, output a decoded list */
294 PyObject *tmp = PyList_GetItem(bins, start);
291 PyObject *tmp = PyList_GetItem(bins, start);
295 if (!tmp)
292 if (!tmp)
296 return NULL;
293 return NULL;
297 if (PyObject_AsCharBuffer(tmp, &buffer, &blen))
294 if (PyObject_AsCharBuffer(tmp, &buffer, &blen))
298 return NULL;
295 return NULL;
299 return decode(buffer, blen);
296 return decode(buffer, blen);
300 }
297 }
301
298
302 /* divide and conquer, memory management is elsewhere */
299 /* divide and conquer, memory management is elsewhere */
303 len = (end - start) / 2;
300 len = (end - start) / 2;
304 return combine(fold(bins, start, start + len),
301 return combine(fold(bins, start, start + len),
305 fold(bins, start + len, end));
302 fold(bins, start + len, end));
306 }
303 }
307
304
308 static PyObject *
305 static PyObject *
309 patches(PyObject *self, PyObject *args)
306 patches(PyObject *self, PyObject *args)
310 {
307 {
311 PyObject *text, *bins, *result;
308 PyObject *text, *bins, *result;
312 struct flist *patch;
309 struct flist *patch;
313 const char *in;
310 const char *in;
314 char *out;
311 char *out;
315 Py_ssize_t len, outlen, inlen;
312 Py_ssize_t len, outlen, inlen;
316
313
317 if (!PyArg_ParseTuple(args, "OO:mpatch", &text, &bins))
314 if (!PyArg_ParseTuple(args, "OO:mpatch", &text, &bins))
318 return NULL;
315 return NULL;
319
316
320 len = PyList_Size(bins);
317 len = PyList_Size(bins);
321 if (!len) {
318 if (!len) {
322 /* nothing to do */
319 /* nothing to do */
323 Py_INCREF(text);
320 Py_INCREF(text);
324 return text;
321 return text;
325 }
322 }
326
323
327 if (PyObject_AsCharBuffer(text, &in, &inlen))
324 if (PyObject_AsCharBuffer(text, &in, &inlen))
328 return NULL;
325 return NULL;
329
326
330 patch = fold(bins, 0, len);
327 patch = fold(bins, 0, len);
331 if (!patch)
328 if (!patch)
332 return NULL;
329 return NULL;
333
330
334 outlen = calcsize(inlen, patch);
331 outlen = calcsize(inlen, patch);
335 if (outlen < 0) {
332 if (outlen < 0) {
336 result = NULL;
333 result = NULL;
337 goto cleanup;
334 goto cleanup;
338 }
335 }
339 result = PyBytes_FromStringAndSize(NULL, outlen);
336 result = PyBytes_FromStringAndSize(NULL, outlen);
340 if (!result) {
337 if (!result) {
341 result = NULL;
338 result = NULL;
342 goto cleanup;
339 goto cleanup;
343 }
340 }
344 out = PyBytes_AsString(result);
341 out = PyBytes_AsString(result);
345 if (!apply(out, in, inlen, patch)) {
342 if (!apply(out, in, inlen, patch)) {
346 Py_DECREF(result);
343 Py_DECREF(result);
347 result = NULL;
344 result = NULL;
348 }
345 }
349 cleanup:
346 cleanup:
350 lfree(patch);
347 lfree(patch);
351 return result;
348 return result;
352 }
349 }
353
350
354 /* calculate size of a patched file directly */
351 /* calculate size of a patched file directly */
355 static PyObject *
352 static PyObject *
356 patchedsize(PyObject *self, PyObject *args)
353 patchedsize(PyObject *self, PyObject *args)
357 {
354 {
358 long orig, start, end, len, outlen = 0, last = 0;
355 long orig, start, end, len, outlen = 0, last = 0, pos = 0;
359 Py_ssize_t patchlen;
356 Py_ssize_t patchlen;
360 char *bin, *binend, *data;
357 char *bin;
361
358
362 if (!PyArg_ParseTuple(args, "ls#", &orig, &bin, &patchlen))
359 if (!PyArg_ParseTuple(args, "ls#", &orig, &bin, &patchlen))
363 return NULL;
360 return NULL;
364
361
365 binend = bin + patchlen;
362 while (pos >= 0 && pos < patchlen) {
366 data = bin + 12;
363 start = getbe32(bin + pos);
367
364 end = getbe32(bin + pos + 4);
368 while (data <= binend) {
365 len = getbe32(bin + pos + 8);
369 start = getbe32(bin);
370 end = getbe32(bin + 4);
371 len = getbe32(bin + 8);
372 if (start > end)
366 if (start > end)
373 break; /* sanity check */
367 break; /* sanity check */
374 bin = data + len;
368 pos += 12 + len;
375 if (bin < data)
376 break; /* big data + big (bogus) len can wrap around */
377 data = bin + 12;
378 outlen += start - last;
369 outlen += start - last;
379 last = end;
370 last = end;
380 outlen += len;
371 outlen += len;
381 }
372 }
382
373
383 if (bin != binend) {
374 if (pos != patchlen) {
384 if (!PyErr_Occurred())
375 if (!PyErr_Occurred())
385 PyErr_SetString(mpatch_Error, "patch cannot be decoded");
376 PyErr_SetString(mpatch_Error, "patch cannot be decoded");
386 return NULL;
377 return NULL;
387 }
378 }
388
379
389 outlen += orig - last;
380 outlen += orig - last;
390 return Py_BuildValue("l", outlen);
381 return Py_BuildValue("l", outlen);
391 }
382 }
392
383
393 static PyMethodDef methods[] = {
384 static PyMethodDef methods[] = {
394 {"patches", patches, METH_VARARGS, "apply a series of patches\n"},
385 {"patches", patches, METH_VARARGS, "apply a series of patches\n"},
395 {"patchedsize", patchedsize, METH_VARARGS, "calculed patched size\n"},
386 {"patchedsize", patchedsize, METH_VARARGS, "calculed patched size\n"},
396 {NULL, NULL}
387 {NULL, NULL}
397 };
388 };
398
389
399 #ifdef IS_PY3K
390 #ifdef IS_PY3K
400 static struct PyModuleDef mpatch_module = {
391 static struct PyModuleDef mpatch_module = {
401 PyModuleDef_HEAD_INIT,
392 PyModuleDef_HEAD_INIT,
402 "mpatch",
393 "mpatch",
403 mpatch_doc,
394 mpatch_doc,
404 -1,
395 -1,
405 methods
396 methods
406 };
397 };
407
398
408 PyMODINIT_FUNC PyInit_mpatch(void)
399 PyMODINIT_FUNC PyInit_mpatch(void)
409 {
400 {
410 PyObject *m;
401 PyObject *m;
411
402
412 m = PyModule_Create(&mpatch_module);
403 m = PyModule_Create(&mpatch_module);
413 if (m == NULL)
404 if (m == NULL)
414 return NULL;
405 return NULL;
415
406
416 mpatch_Error = PyErr_NewException("mpatch.mpatchError", NULL, NULL);
407 mpatch_Error = PyErr_NewException("mpatch.mpatchError", NULL, NULL);
417 Py_INCREF(mpatch_Error);
408 Py_INCREF(mpatch_Error);
418 PyModule_AddObject(m, "mpatchError", mpatch_Error);
409 PyModule_AddObject(m, "mpatchError", mpatch_Error);
419
410
420 return m;
411 return m;
421 }
412 }
422 #else
413 #else
423 PyMODINIT_FUNC
414 PyMODINIT_FUNC
424 initmpatch(void)
415 initmpatch(void)
425 {
416 {
426 Py_InitModule3("mpatch", methods, mpatch_doc);
417 Py_InitModule3("mpatch", methods, mpatch_doc);
427 mpatch_Error = PyErr_NewException("mpatch.mpatchError", NULL, NULL);
418 mpatch_Error = PyErr_NewException("mpatch.mpatchError", NULL, NULL);
428 }
419 }
429 #endif
420 #endif
@@ -1,1965 +1,1959 b''
1 /*
1 /*
2 parsers.c - efficient content parsing
2 parsers.c - efficient content parsing
3
3
4 Copyright 2008 Matt Mackall <mpm@selenic.com> and others
4 Copyright 2008 Matt Mackall <mpm@selenic.com> and others
5
5
6 This software may be used and distributed according to the terms of
6 This software may be used and distributed according to the terms of
7 the GNU General Public License, incorporated herein by reference.
7 the GNU General Public License, incorporated herein by reference.
8 */
8 */
9
9
10 #include <Python.h>
10 #include <Python.h>
11 #include <ctype.h>
11 #include <ctype.h>
12 #include <stddef.h>
12 #include <stddef.h>
13 #include <string.h>
13 #include <string.h>
14
14
15 #include "util.h"
15 #include "util.h"
16
16
17 static int8_t hextable[256] = {
17 static int8_t hextable[256] = {
18 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
18 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
19 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
19 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
20 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
20 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
21 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, /* 0-9 */
21 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, /* 0-9 */
22 -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* A-F */
22 -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* A-F */
23 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
23 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
24 -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* a-f */
24 -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* a-f */
25 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
25 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
26 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
26 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
27 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
27 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
28 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
28 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
29 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
29 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
30 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
30 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
31 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
31 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
32 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
32 -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
33 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
34 };
34 };
35
35
36 static inline int hexdigit(const char *p, Py_ssize_t off)
36 static inline int hexdigit(const char *p, Py_ssize_t off)
37 {
37 {
38 int8_t val = hextable[(unsigned char)p[off]];
38 int8_t val = hextable[(unsigned char)p[off]];
39
39
40 if (val >= 0) {
40 if (val >= 0) {
41 return val;
41 return val;
42 }
42 }
43
43
44 PyErr_SetString(PyExc_ValueError, "input contains non-hex character");
44 PyErr_SetString(PyExc_ValueError, "input contains non-hex character");
45 return 0;
45 return 0;
46 }
46 }
47
47
48 /*
48 /*
49 * Turn a hex-encoded string into binary.
49 * Turn a hex-encoded string into binary.
50 */
50 */
51 static PyObject *unhexlify(const char *str, int len)
51 static PyObject *unhexlify(const char *str, int len)
52 {
52 {
53 PyObject *ret;
53 PyObject *ret;
54 char *d;
54 char *d;
55 int i;
55 int i;
56
56
57 ret = PyBytes_FromStringAndSize(NULL, len / 2);
57 ret = PyBytes_FromStringAndSize(NULL, len / 2);
58
58
59 if (!ret)
59 if (!ret)
60 return NULL;
60 return NULL;
61
61
62 d = PyBytes_AsString(ret);
62 d = PyBytes_AsString(ret);
63
63
64 for (i = 0; i < len;) {
64 for (i = 0; i < len;) {
65 int hi = hexdigit(str, i++);
65 int hi = hexdigit(str, i++);
66 int lo = hexdigit(str, i++);
66 int lo = hexdigit(str, i++);
67 *d++ = (hi << 4) | lo;
67 *d++ = (hi << 4) | lo;
68 }
68 }
69
69
70 return ret;
70 return ret;
71 }
71 }
72
72
73 /*
73 /*
74 * This code assumes that a manifest is stitched together with newline
74 * This code assumes that a manifest is stitched together with newline
75 * ('\n') characters.
75 * ('\n') characters.
76 */
76 */
77 static PyObject *parse_manifest(PyObject *self, PyObject *args)
77 static PyObject *parse_manifest(PyObject *self, PyObject *args)
78 {
78 {
79 PyObject *mfdict, *fdict;
79 PyObject *mfdict, *fdict;
80 char *str, *start, *end;
80 char *str, *start, *end;
81 int len;
81 int len;
82
82
83 if (!PyArg_ParseTuple(args, "O!O!s#:parse_manifest",
83 if (!PyArg_ParseTuple(args, "O!O!s#:parse_manifest",
84 &PyDict_Type, &mfdict,
84 &PyDict_Type, &mfdict,
85 &PyDict_Type, &fdict,
85 &PyDict_Type, &fdict,
86 &str, &len))
86 &str, &len))
87 goto quit;
87 goto quit;
88
88
89 start = str;
89 start = str;
90 end = str + len;
90 end = str + len;
91 while (start < end) {
91 while (start < end) {
92 PyObject *file = NULL, *node = NULL;
92 PyObject *file = NULL, *node = NULL;
93 PyObject *flags = NULL;
93 PyObject *flags = NULL;
94 char *zero = NULL, *newline = NULL;
94 char *zero = NULL, *newline = NULL;
95 ptrdiff_t nlen;
95 ptrdiff_t nlen;
96
96
97 zero = memchr(start, '\0', end - start);
97 zero = memchr(start, '\0', end - start);
98 if (!zero) {
98 if (!zero) {
99 PyErr_SetString(PyExc_ValueError,
99 PyErr_SetString(PyExc_ValueError,
100 "manifest entry has no separator");
100 "manifest entry has no separator");
101 goto quit;
101 goto quit;
102 }
102 }
103
103
104 newline = memchr(zero + 1, '\n', end - (zero + 1));
104 newline = memchr(zero + 1, '\n', end - (zero + 1));
105 if (!newline) {
105 if (!newline) {
106 PyErr_SetString(PyExc_ValueError,
106 PyErr_SetString(PyExc_ValueError,
107 "manifest contains trailing garbage");
107 "manifest contains trailing garbage");
108 goto quit;
108 goto quit;
109 }
109 }
110
110
111 file = PyBytes_FromStringAndSize(start, zero - start);
111 file = PyBytes_FromStringAndSize(start, zero - start);
112
112
113 if (!file)
113 if (!file)
114 goto bail;
114 goto bail;
115
115
116 nlen = newline - zero - 1;
116 nlen = newline - zero - 1;
117
117
118 node = unhexlify(zero + 1, nlen > 40 ? 40 : (int)nlen);
118 node = unhexlify(zero + 1, nlen > 40 ? 40 : (int)nlen);
119 if (!node)
119 if (!node)
120 goto bail;
120 goto bail;
121
121
122 if (nlen > 40) {
122 if (nlen > 40) {
123 flags = PyBytes_FromStringAndSize(zero + 41,
123 flags = PyBytes_FromStringAndSize(zero + 41,
124 nlen - 40);
124 nlen - 40);
125 if (!flags)
125 if (!flags)
126 goto bail;
126 goto bail;
127
127
128 if (PyDict_SetItem(fdict, file, flags) == -1)
128 if (PyDict_SetItem(fdict, file, flags) == -1)
129 goto bail;
129 goto bail;
130 }
130 }
131
131
132 if (PyDict_SetItem(mfdict, file, node) == -1)
132 if (PyDict_SetItem(mfdict, file, node) == -1)
133 goto bail;
133 goto bail;
134
134
135 start = newline + 1;
135 start = newline + 1;
136
136
137 Py_XDECREF(flags);
137 Py_XDECREF(flags);
138 Py_XDECREF(node);
138 Py_XDECREF(node);
139 Py_XDECREF(file);
139 Py_XDECREF(file);
140 continue;
140 continue;
141 bail:
141 bail:
142 Py_XDECREF(flags);
142 Py_XDECREF(flags);
143 Py_XDECREF(node);
143 Py_XDECREF(node);
144 Py_XDECREF(file);
144 Py_XDECREF(file);
145 goto quit;
145 goto quit;
146 }
146 }
147
147
148 Py_INCREF(Py_None);
148 Py_INCREF(Py_None);
149 return Py_None;
149 return Py_None;
150 quit:
150 quit:
151 return NULL;
151 return NULL;
152 }
152 }
153
153
154 static PyObject *parse_dirstate(PyObject *self, PyObject *args)
154 static PyObject *parse_dirstate(PyObject *self, PyObject *args)
155 {
155 {
156 PyObject *dmap, *cmap, *parents = NULL, *ret = NULL;
156 PyObject *dmap, *cmap, *parents = NULL, *ret = NULL;
157 PyObject *fname = NULL, *cname = NULL, *entry = NULL;
157 PyObject *fname = NULL, *cname = NULL, *entry = NULL;
158 char state, *str, *cur, *end, *cpos;
158 char state, *cur, *str, *cpos;
159 int mode, size, mtime;
159 int mode, size, mtime;
160 unsigned int flen;
160 unsigned int flen;
161 int len;
161 int len, pos = 40;
162
162
163 if (!PyArg_ParseTuple(args, "O!O!s#:parse_dirstate",
163 if (!PyArg_ParseTuple(args, "O!O!s#:parse_dirstate",
164 &PyDict_Type, &dmap,
164 &PyDict_Type, &dmap,
165 &PyDict_Type, &cmap,
165 &PyDict_Type, &cmap,
166 &str, &len))
166 &str, &len))
167 goto quit;
167 goto quit;
168
168
169 /* read parents */
169 /* read parents */
170 if (len < 40)
170 if (len < 40)
171 goto quit;
171 goto quit;
172
172
173 parents = Py_BuildValue("s#s#", str, 20, str + 20, 20);
173 parents = Py_BuildValue("s#s#", str, 20, str + 20, 20);
174 if (!parents)
174 if (!parents)
175 goto quit;
175 goto quit;
176
176
177 /* read filenames */
177 /* read filenames */
178 cur = str + 40;
178 while (pos >= 40 && pos < len) {
179 end = str + len;
179 cur = str + pos;
180
181 while (cur < end - 17) {
182 /* unpack header */
180 /* unpack header */
183 state = *cur;
181 state = *cur;
184 mode = getbe32(cur + 1);
182 mode = getbe32(cur + 1);
185 size = getbe32(cur + 5);
183 size = getbe32(cur + 5);
186 mtime = getbe32(cur + 9);
184 mtime = getbe32(cur + 9);
187 flen = getbe32(cur + 13);
185 flen = getbe32(cur + 13);
186 pos += 17;
188 cur += 17;
187 cur += 17;
189 if (cur + flen > end || cur + flen < cur) {
188 if (flen > len - pos || flen < 0) {
190 PyErr_SetString(PyExc_ValueError, "overflow in dirstate");
189 PyErr_SetString(PyExc_ValueError, "overflow in dirstate");
191 goto quit;
190 goto quit;
192 }
191 }
193
192
194 entry = Py_BuildValue("ciii", state, mode, size, mtime);
193 entry = Py_BuildValue("ciii", state, mode, size, mtime);
195 if (!entry)
194 if (!entry)
196 goto quit;
195 goto quit;
197 PyObject_GC_UnTrack(entry); /* don't waste time with this */
196 PyObject_GC_UnTrack(entry); /* don't waste time with this */
198
197
199 cpos = memchr(cur, 0, flen);
198 cpos = memchr(cur, 0, flen);
200 if (cpos) {
199 if (cpos) {
201 fname = PyBytes_FromStringAndSize(cur, cpos - cur);
200 fname = PyBytes_FromStringAndSize(cur, cpos - cur);
202 cname = PyBytes_FromStringAndSize(cpos + 1,
201 cname = PyBytes_FromStringAndSize(cpos + 1,
203 flen - (cpos - cur) - 1);
202 flen - (cpos - cur) - 1);
204 if (!fname || !cname ||
203 if (!fname || !cname ||
205 PyDict_SetItem(cmap, fname, cname) == -1 ||
204 PyDict_SetItem(cmap, fname, cname) == -1 ||
206 PyDict_SetItem(dmap, fname, entry) == -1)
205 PyDict_SetItem(dmap, fname, entry) == -1)
207 goto quit;
206 goto quit;
208 Py_DECREF(cname);
207 Py_DECREF(cname);
209 } else {
208 } else {
210 fname = PyBytes_FromStringAndSize(cur, flen);
209 fname = PyBytes_FromStringAndSize(cur, flen);
211 if (!fname ||
210 if (!fname ||
212 PyDict_SetItem(dmap, fname, entry) == -1)
211 PyDict_SetItem(dmap, fname, entry) == -1)
213 goto quit;
212 goto quit;
214 }
213 }
215 cur += flen;
216 Py_DECREF(fname);
214 Py_DECREF(fname);
217 Py_DECREF(entry);
215 Py_DECREF(entry);
218 fname = cname = entry = NULL;
216 fname = cname = entry = NULL;
217 pos += flen;
219 }
218 }
220
219
221 ret = parents;
220 ret = parents;
222 Py_INCREF(ret);
221 Py_INCREF(ret);
223 quit:
222 quit:
224 Py_XDECREF(fname);
223 Py_XDECREF(fname);
225 Py_XDECREF(cname);
224 Py_XDECREF(cname);
226 Py_XDECREF(entry);
225 Py_XDECREF(entry);
227 Py_XDECREF(parents);
226 Py_XDECREF(parents);
228 return ret;
227 return ret;
229 }
228 }
230
229
231 static inline int getintat(PyObject *tuple, int off, uint32_t *v)
230 static inline int getintat(PyObject *tuple, int off, uint32_t *v)
232 {
231 {
233 PyObject *o = PyTuple_GET_ITEM(tuple, off);
232 PyObject *o = PyTuple_GET_ITEM(tuple, off);
234 long val;
233 long val;
235
234
236 if (PyInt_Check(o))
235 if (PyInt_Check(o))
237 val = PyInt_AS_LONG(o);
236 val = PyInt_AS_LONG(o);
238 else if (PyLong_Check(o)) {
237 else if (PyLong_Check(o)) {
239 val = PyLong_AsLong(o);
238 val = PyLong_AsLong(o);
240 if (val == -1 && PyErr_Occurred())
239 if (val == -1 && PyErr_Occurred())
241 return -1;
240 return -1;
242 } else {
241 } else {
243 PyErr_SetString(PyExc_TypeError, "expected an int or long");
242 PyErr_SetString(PyExc_TypeError, "expected an int or long");
244 return -1;
243 return -1;
245 }
244 }
246 if (LONG_MAX > INT_MAX && (val > INT_MAX || val < INT_MIN)) {
245 if (LONG_MAX > INT_MAX && (val > INT_MAX || val < INT_MIN)) {
247 PyErr_SetString(PyExc_OverflowError,
246 PyErr_SetString(PyExc_OverflowError,
248 "Python value to large to convert to uint32_t");
247 "Python value to large to convert to uint32_t");
249 return -1;
248 return -1;
250 }
249 }
251 *v = (uint32_t)val;
250 *v = (uint32_t)val;
252 return 0;
251 return 0;
253 }
252 }
254
253
255 static PyObject *dirstate_unset;
254 static PyObject *dirstate_unset;
256
255
257 /*
256 /*
258 * Efficiently pack a dirstate object into its on-disk format.
257 * Efficiently pack a dirstate object into its on-disk format.
259 */
258 */
260 static PyObject *pack_dirstate(PyObject *self, PyObject *args)
259 static PyObject *pack_dirstate(PyObject *self, PyObject *args)
261 {
260 {
262 PyObject *packobj = NULL;
261 PyObject *packobj = NULL;
263 PyObject *map, *copymap, *pl;
262 PyObject *map, *copymap, *pl;
264 Py_ssize_t nbytes, pos, l;
263 Py_ssize_t nbytes, pos, l;
265 PyObject *k, *v, *pn;
264 PyObject *k, *v, *pn;
266 char *p, *s;
265 char *p, *s;
267 double now;
266 double now;
268
267
269 if (!PyArg_ParseTuple(args, "O!O!Od:pack_dirstate",
268 if (!PyArg_ParseTuple(args, "O!O!Od:pack_dirstate",
270 &PyDict_Type, &map, &PyDict_Type, &copymap,
269 &PyDict_Type, &map, &PyDict_Type, &copymap,
271 &pl, &now))
270 &pl, &now))
272 return NULL;
271 return NULL;
273
272
274 if (!PySequence_Check(pl) || PySequence_Size(pl) != 2) {
273 if (!PySequence_Check(pl) || PySequence_Size(pl) != 2) {
275 PyErr_SetString(PyExc_TypeError, "expected 2-element sequence");
274 PyErr_SetString(PyExc_TypeError, "expected 2-element sequence");
276 return NULL;
275 return NULL;
277 }
276 }
278
277
279 /* Figure out how much we need to allocate. */
278 /* Figure out how much we need to allocate. */
280 for (nbytes = 40, pos = 0; PyDict_Next(map, &pos, &k, &v);) {
279 for (nbytes = 40, pos = 0; PyDict_Next(map, &pos, &k, &v);) {
281 PyObject *c;
280 PyObject *c;
282 if (!PyString_Check(k)) {
281 if (!PyString_Check(k)) {
283 PyErr_SetString(PyExc_TypeError, "expected string key");
282 PyErr_SetString(PyExc_TypeError, "expected string key");
284 goto bail;
283 goto bail;
285 }
284 }
286 nbytes += PyString_GET_SIZE(k) + 17;
285 nbytes += PyString_GET_SIZE(k) + 17;
287 c = PyDict_GetItem(copymap, k);
286 c = PyDict_GetItem(copymap, k);
288 if (c) {
287 if (c) {
289 if (!PyString_Check(c)) {
288 if (!PyString_Check(c)) {
290 PyErr_SetString(PyExc_TypeError,
289 PyErr_SetString(PyExc_TypeError,
291 "expected string key");
290 "expected string key");
292 goto bail;
291 goto bail;
293 }
292 }
294 nbytes += PyString_GET_SIZE(c) + 1;
293 nbytes += PyString_GET_SIZE(c) + 1;
295 }
294 }
296 }
295 }
297
296
298 packobj = PyString_FromStringAndSize(NULL, nbytes);
297 packobj = PyString_FromStringAndSize(NULL, nbytes);
299 if (packobj == NULL)
298 if (packobj == NULL)
300 goto bail;
299 goto bail;
301
300
302 p = PyString_AS_STRING(packobj);
301 p = PyString_AS_STRING(packobj);
303
302
304 pn = PySequence_ITEM(pl, 0);
303 pn = PySequence_ITEM(pl, 0);
305 if (PyString_AsStringAndSize(pn, &s, &l) == -1 || l != 20) {
304 if (PyString_AsStringAndSize(pn, &s, &l) == -1 || l != 20) {
306 PyErr_SetString(PyExc_TypeError, "expected a 20-byte hash");
305 PyErr_SetString(PyExc_TypeError, "expected a 20-byte hash");
307 goto bail;
306 goto bail;
308 }
307 }
309 memcpy(p, s, l);
308 memcpy(p, s, l);
310 p += 20;
309 p += 20;
311 pn = PySequence_ITEM(pl, 1);
310 pn = PySequence_ITEM(pl, 1);
312 if (PyString_AsStringAndSize(pn, &s, &l) == -1 || l != 20) {
311 if (PyString_AsStringAndSize(pn, &s, &l) == -1 || l != 20) {
313 PyErr_SetString(PyExc_TypeError, "expected a 20-byte hash");
312 PyErr_SetString(PyExc_TypeError, "expected a 20-byte hash");
314 goto bail;
313 goto bail;
315 }
314 }
316 memcpy(p, s, l);
315 memcpy(p, s, l);
317 p += 20;
316 p += 20;
318
317
319 for (pos = 0; PyDict_Next(map, &pos, &k, &v); ) {
318 for (pos = 0; PyDict_Next(map, &pos, &k, &v); ) {
320 uint32_t mode, size, mtime;
319 uint32_t mode, size, mtime;
321 Py_ssize_t len, l;
320 Py_ssize_t len, l;
322 PyObject *o;
321 PyObject *o;
323 char *s, *t;
322 char *s, *t;
324
323
325 if (!PyTuple_Check(v) || PyTuple_GET_SIZE(v) != 4) {
324 if (!PyTuple_Check(v) || PyTuple_GET_SIZE(v) != 4) {
326 PyErr_SetString(PyExc_TypeError, "expected a 4-tuple");
325 PyErr_SetString(PyExc_TypeError, "expected a 4-tuple");
327 goto bail;
326 goto bail;
328 }
327 }
329 o = PyTuple_GET_ITEM(v, 0);
328 o = PyTuple_GET_ITEM(v, 0);
330 if (PyString_AsStringAndSize(o, &s, &l) == -1 || l != 1) {
329 if (PyString_AsStringAndSize(o, &s, &l) == -1 || l != 1) {
331 PyErr_SetString(PyExc_TypeError, "expected one byte");
330 PyErr_SetString(PyExc_TypeError, "expected one byte");
332 goto bail;
331 goto bail;
333 }
332 }
334 *p++ = *s;
333 *p++ = *s;
335 if (getintat(v, 1, &mode) == -1)
334 if (getintat(v, 1, &mode) == -1)
336 goto bail;
335 goto bail;
337 if (getintat(v, 2, &size) == -1)
336 if (getintat(v, 2, &size) == -1)
338 goto bail;
337 goto bail;
339 if (getintat(v, 3, &mtime) == -1)
338 if (getintat(v, 3, &mtime) == -1)
340 goto bail;
339 goto bail;
341 if (*s == 'n' && mtime == (uint32_t)now) {
340 if (*s == 'n' && mtime == (uint32_t)now) {
342 /* See pure/parsers.py:pack_dirstate for why we do
341 /* See pure/parsers.py:pack_dirstate for why we do
343 * this. */
342 * this. */
344 if (PyDict_SetItem(map, k, dirstate_unset) == -1)
343 if (PyDict_SetItem(map, k, dirstate_unset) == -1)
345 goto bail;
344 goto bail;
346 mtime = -1;
345 mtime = -1;
347 }
346 }
348 putbe32(mode, p);
347 putbe32(mode, p);
349 putbe32(size, p + 4);
348 putbe32(size, p + 4);
350 putbe32(mtime, p + 8);
349 putbe32(mtime, p + 8);
351 t = p + 12;
350 t = p + 12;
352 p += 16;
351 p += 16;
353 len = PyString_GET_SIZE(k);
352 len = PyString_GET_SIZE(k);
354 memcpy(p, PyString_AS_STRING(k), len);
353 memcpy(p, PyString_AS_STRING(k), len);
355 p += len;
354 p += len;
356 o = PyDict_GetItem(copymap, k);
355 o = PyDict_GetItem(copymap, k);
357 if (o) {
356 if (o) {
358 *p++ = '\0';
357 *p++ = '\0';
359 l = PyString_GET_SIZE(o);
358 l = PyString_GET_SIZE(o);
360 memcpy(p, PyString_AS_STRING(o), l);
359 memcpy(p, PyString_AS_STRING(o), l);
361 p += l;
360 p += l;
362 len += l + 1;
361 len += l + 1;
363 }
362 }
364 putbe32((uint32_t)len, t);
363 putbe32((uint32_t)len, t);
365 }
364 }
366
365
367 pos = p - PyString_AS_STRING(packobj);
366 pos = p - PyString_AS_STRING(packobj);
368 if (pos != nbytes) {
367 if (pos != nbytes) {
369 PyErr_Format(PyExc_SystemError, "bad dirstate size: %ld != %ld",
368 PyErr_Format(PyExc_SystemError, "bad dirstate size: %ld != %ld",
370 (long)pos, (long)nbytes);
369 (long)pos, (long)nbytes);
371 goto bail;
370 goto bail;
372 }
371 }
373
372
374 return packobj;
373 return packobj;
375 bail:
374 bail:
376 Py_XDECREF(packobj);
375 Py_XDECREF(packobj);
377 return NULL;
376 return NULL;
378 }
377 }
379
378
380 /*
379 /*
381 * A base-16 trie for fast node->rev mapping.
380 * A base-16 trie for fast node->rev mapping.
382 *
381 *
383 * Positive value is index of the next node in the trie
382 * Positive value is index of the next node in the trie
384 * Negative value is a leaf: -(rev + 1)
383 * Negative value is a leaf: -(rev + 1)
385 * Zero is empty
384 * Zero is empty
386 */
385 */
387 typedef struct {
386 typedef struct {
388 int children[16];
387 int children[16];
389 } nodetree;
388 } nodetree;
390
389
391 /*
390 /*
392 * This class has two behaviours.
391 * This class has two behaviours.
393 *
392 *
394 * When used in a list-like way (with integer keys), we decode an
393 * When used in a list-like way (with integer keys), we decode an
395 * entry in a RevlogNG index file on demand. Our last entry is a
394 * entry in a RevlogNG index file on demand. Our last entry is a
396 * sentinel, always a nullid. We have limited support for
395 * sentinel, always a nullid. We have limited support for
397 * integer-keyed insert and delete, only at elements right before the
396 * integer-keyed insert and delete, only at elements right before the
398 * sentinel.
397 * sentinel.
399 *
398 *
400 * With string keys, we lazily perform a reverse mapping from node to
399 * With string keys, we lazily perform a reverse mapping from node to
401 * rev, using a base-16 trie.
400 * rev, using a base-16 trie.
402 */
401 */
403 typedef struct {
402 typedef struct {
404 PyObject_HEAD
403 PyObject_HEAD
405 /* Type-specific fields go here. */
404 /* Type-specific fields go here. */
406 PyObject *data; /* raw bytes of index */
405 PyObject *data; /* raw bytes of index */
407 PyObject **cache; /* cached tuples */
406 PyObject **cache; /* cached tuples */
408 const char **offsets; /* populated on demand */
407 const char **offsets; /* populated on demand */
409 Py_ssize_t raw_length; /* original number of elements */
408 Py_ssize_t raw_length; /* original number of elements */
410 Py_ssize_t length; /* current number of elements */
409 Py_ssize_t length; /* current number of elements */
411 PyObject *added; /* populated on demand */
410 PyObject *added; /* populated on demand */
412 PyObject *headrevs; /* cache, invalidated on changes */
411 PyObject *headrevs; /* cache, invalidated on changes */
413 nodetree *nt; /* base-16 trie */
412 nodetree *nt; /* base-16 trie */
414 int ntlength; /* # nodes in use */
413 int ntlength; /* # nodes in use */
415 int ntcapacity; /* # nodes allocated */
414 int ntcapacity; /* # nodes allocated */
416 int ntdepth; /* maximum depth of tree */
415 int ntdepth; /* maximum depth of tree */
417 int ntsplits; /* # splits performed */
416 int ntsplits; /* # splits performed */
418 int ntrev; /* last rev scanned */
417 int ntrev; /* last rev scanned */
419 int ntlookups; /* # lookups */
418 int ntlookups; /* # lookups */
420 int ntmisses; /* # lookups that miss the cache */
419 int ntmisses; /* # lookups that miss the cache */
421 int inlined;
420 int inlined;
422 } indexObject;
421 } indexObject;
423
422
424 static Py_ssize_t index_length(const indexObject *self)
423 static Py_ssize_t index_length(const indexObject *self)
425 {
424 {
426 if (self->added == NULL)
425 if (self->added == NULL)
427 return self->length;
426 return self->length;
428 return self->length + PyList_GET_SIZE(self->added);
427 return self->length + PyList_GET_SIZE(self->added);
429 }
428 }
430
429
431 static PyObject *nullentry;
430 static PyObject *nullentry;
432 static const char nullid[20];
431 static const char nullid[20];
433
432
434 static long inline_scan(indexObject *self, const char **offsets);
433 static long inline_scan(indexObject *self, const char **offsets);
435
434
436 #if LONG_MAX == 0x7fffffffL
435 #if LONG_MAX == 0x7fffffffL
437 static char *tuple_format = "Kiiiiiis#";
436 static char *tuple_format = "Kiiiiiis#";
438 #else
437 #else
439 static char *tuple_format = "kiiiiiis#";
438 static char *tuple_format = "kiiiiiis#";
440 #endif
439 #endif
441
440
442 /* A RevlogNG v1 index entry is 64 bytes long. */
441 /* A RevlogNG v1 index entry is 64 bytes long. */
443 static const long v1_hdrsize = 64;
442 static const long v1_hdrsize = 64;
444
443
445 /*
444 /*
446 * Return a pointer to the beginning of a RevlogNG record.
445 * Return a pointer to the beginning of a RevlogNG record.
447 */
446 */
448 static const char *index_deref(indexObject *self, Py_ssize_t pos)
447 static const char *index_deref(indexObject *self, Py_ssize_t pos)
449 {
448 {
450 if (self->inlined && pos > 0) {
449 if (self->inlined && pos > 0) {
451 if (self->offsets == NULL) {
450 if (self->offsets == NULL) {
452 self->offsets = malloc(self->raw_length *
451 self->offsets = malloc(self->raw_length *
453 sizeof(*self->offsets));
452 sizeof(*self->offsets));
454 if (self->offsets == NULL)
453 if (self->offsets == NULL)
455 return (const char *)PyErr_NoMemory();
454 return (const char *)PyErr_NoMemory();
456 inline_scan(self, self->offsets);
455 inline_scan(self, self->offsets);
457 }
456 }
458 return self->offsets[pos];
457 return self->offsets[pos];
459 }
458 }
460
459
461 return PyString_AS_STRING(self->data) + pos * v1_hdrsize;
460 return PyString_AS_STRING(self->data) + pos * v1_hdrsize;
462 }
461 }
463
462
464 /*
463 /*
465 * RevlogNG format (all in big endian, data may be inlined):
464 * RevlogNG format (all in big endian, data may be inlined):
466 * 6 bytes: offset
465 * 6 bytes: offset
467 * 2 bytes: flags
466 * 2 bytes: flags
468 * 4 bytes: compressed length
467 * 4 bytes: compressed length
469 * 4 bytes: uncompressed length
468 * 4 bytes: uncompressed length
470 * 4 bytes: base revision
469 * 4 bytes: base revision
471 * 4 bytes: link revision
470 * 4 bytes: link revision
472 * 4 bytes: parent 1 revision
471 * 4 bytes: parent 1 revision
473 * 4 bytes: parent 2 revision
472 * 4 bytes: parent 2 revision
474 * 32 bytes: nodeid (only 20 bytes used)
473 * 32 bytes: nodeid (only 20 bytes used)
475 */
474 */
476 static PyObject *index_get(indexObject *self, Py_ssize_t pos)
475 static PyObject *index_get(indexObject *self, Py_ssize_t pos)
477 {
476 {
478 uint64_t offset_flags;
477 uint64_t offset_flags;
479 int comp_len, uncomp_len, base_rev, link_rev, parent_1, parent_2;
478 int comp_len, uncomp_len, base_rev, link_rev, parent_1, parent_2;
480 const char *c_node_id;
479 const char *c_node_id;
481 const char *data;
480 const char *data;
482 Py_ssize_t length = index_length(self);
481 Py_ssize_t length = index_length(self);
483 PyObject *entry;
482 PyObject *entry;
484
483
485 if (pos < 0)
484 if (pos < 0)
486 pos += length;
485 pos += length;
487
486
488 if (pos < 0 || pos >= length) {
487 if (pos < 0 || pos >= length) {
489 PyErr_SetString(PyExc_IndexError, "revlog index out of range");
488 PyErr_SetString(PyExc_IndexError, "revlog index out of range");
490 return NULL;
489 return NULL;
491 }
490 }
492
491
493 if (pos == length - 1) {
492 if (pos == length - 1) {
494 Py_INCREF(nullentry);
493 Py_INCREF(nullentry);
495 return nullentry;
494 return nullentry;
496 }
495 }
497
496
498 if (pos >= self->length - 1) {
497 if (pos >= self->length - 1) {
499 PyObject *obj;
498 PyObject *obj;
500 obj = PyList_GET_ITEM(self->added, pos - self->length + 1);
499 obj = PyList_GET_ITEM(self->added, pos - self->length + 1);
501 Py_INCREF(obj);
500 Py_INCREF(obj);
502 return obj;
501 return obj;
503 }
502 }
504
503
505 if (self->cache) {
504 if (self->cache) {
506 if (self->cache[pos]) {
505 if (self->cache[pos]) {
507 Py_INCREF(self->cache[pos]);
506 Py_INCREF(self->cache[pos]);
508 return self->cache[pos];
507 return self->cache[pos];
509 }
508 }
510 } else {
509 } else {
511 self->cache = calloc(self->raw_length, sizeof(PyObject *));
510 self->cache = calloc(self->raw_length, sizeof(PyObject *));
512 if (self->cache == NULL)
511 if (self->cache == NULL)
513 return PyErr_NoMemory();
512 return PyErr_NoMemory();
514 }
513 }
515
514
516 data = index_deref(self, pos);
515 data = index_deref(self, pos);
517 if (data == NULL)
516 if (data == NULL)
518 return NULL;
517 return NULL;
519
518
520 offset_flags = getbe32(data + 4);
519 offset_flags = getbe32(data + 4);
521 if (pos == 0) /* mask out version number for the first entry */
520 if (pos == 0) /* mask out version number for the first entry */
522 offset_flags &= 0xFFFF;
521 offset_flags &= 0xFFFF;
523 else {
522 else {
524 uint32_t offset_high = getbe32(data);
523 uint32_t offset_high = getbe32(data);
525 offset_flags |= ((uint64_t)offset_high) << 32;
524 offset_flags |= ((uint64_t)offset_high) << 32;
526 }
525 }
527
526
528 comp_len = getbe32(data + 8);
527 comp_len = getbe32(data + 8);
529 uncomp_len = getbe32(data + 12);
528 uncomp_len = getbe32(data + 12);
530 base_rev = getbe32(data + 16);
529 base_rev = getbe32(data + 16);
531 link_rev = getbe32(data + 20);
530 link_rev = getbe32(data + 20);
532 parent_1 = getbe32(data + 24);
531 parent_1 = getbe32(data + 24);
533 parent_2 = getbe32(data + 28);
532 parent_2 = getbe32(data + 28);
534 c_node_id = data + 32;
533 c_node_id = data + 32;
535
534
536 entry = Py_BuildValue(tuple_format, offset_flags, comp_len,
535 entry = Py_BuildValue(tuple_format, offset_flags, comp_len,
537 uncomp_len, base_rev, link_rev,
536 uncomp_len, base_rev, link_rev,
538 parent_1, parent_2, c_node_id, 20);
537 parent_1, parent_2, c_node_id, 20);
539
538
540 if (entry) {
539 if (entry) {
541 PyObject_GC_UnTrack(entry);
540 PyObject_GC_UnTrack(entry);
542 Py_INCREF(entry);
541 Py_INCREF(entry);
543 }
542 }
544
543
545 self->cache[pos] = entry;
544 self->cache[pos] = entry;
546
545
547 return entry;
546 return entry;
548 }
547 }
549
548
550 /*
549 /*
551 * Return the 20-byte SHA of the node corresponding to the given rev.
550 * Return the 20-byte SHA of the node corresponding to the given rev.
552 */
551 */
553 static const char *index_node(indexObject *self, Py_ssize_t pos)
552 static const char *index_node(indexObject *self, Py_ssize_t pos)
554 {
553 {
555 Py_ssize_t length = index_length(self);
554 Py_ssize_t length = index_length(self);
556 const char *data;
555 const char *data;
557
556
558 if (pos == length - 1 || pos == INT_MAX)
557 if (pos == length - 1 || pos == INT_MAX)
559 return nullid;
558 return nullid;
560
559
561 if (pos >= length)
560 if (pos >= length)
562 return NULL;
561 return NULL;
563
562
564 if (pos >= self->length - 1) {
563 if (pos >= self->length - 1) {
565 PyObject *tuple, *str;
564 PyObject *tuple, *str;
566 tuple = PyList_GET_ITEM(self->added, pos - self->length + 1);
565 tuple = PyList_GET_ITEM(self->added, pos - self->length + 1);
567 str = PyTuple_GetItem(tuple, 7);
566 str = PyTuple_GetItem(tuple, 7);
568 return str ? PyString_AS_STRING(str) : NULL;
567 return str ? PyString_AS_STRING(str) : NULL;
569 }
568 }
570
569
571 data = index_deref(self, pos);
570 data = index_deref(self, pos);
572 return data ? data + 32 : NULL;
571 return data ? data + 32 : NULL;
573 }
572 }
574
573
575 static int nt_insert(indexObject *self, const char *node, int rev);
574 static int nt_insert(indexObject *self, const char *node, int rev);
576
575
577 static int node_check(PyObject *obj, char **node, Py_ssize_t *nodelen)
576 static int node_check(PyObject *obj, char **node, Py_ssize_t *nodelen)
578 {
577 {
579 if (PyString_AsStringAndSize(obj, node, nodelen) == -1)
578 if (PyString_AsStringAndSize(obj, node, nodelen) == -1)
580 return -1;
579 return -1;
581 if (*nodelen == 20)
580 if (*nodelen == 20)
582 return 0;
581 return 0;
583 PyErr_SetString(PyExc_ValueError, "20-byte hash required");
582 PyErr_SetString(PyExc_ValueError, "20-byte hash required");
584 return -1;
583 return -1;
585 }
584 }
586
585
587 static PyObject *index_insert(indexObject *self, PyObject *args)
586 static PyObject *index_insert(indexObject *self, PyObject *args)
588 {
587 {
589 PyObject *obj;
588 PyObject *obj;
590 char *node;
589 char *node;
591 long offset;
590 long offset;
592 Py_ssize_t len, nodelen;
591 Py_ssize_t len, nodelen;
593
592
594 if (!PyArg_ParseTuple(args, "lO", &offset, &obj))
593 if (!PyArg_ParseTuple(args, "lO", &offset, &obj))
595 return NULL;
594 return NULL;
596
595
597 if (!PyTuple_Check(obj) || PyTuple_GET_SIZE(obj) != 8) {
596 if (!PyTuple_Check(obj) || PyTuple_GET_SIZE(obj) != 8) {
598 PyErr_SetString(PyExc_TypeError, "8-tuple required");
597 PyErr_SetString(PyExc_TypeError, "8-tuple required");
599 return NULL;
598 return NULL;
600 }
599 }
601
600
602 if (node_check(PyTuple_GET_ITEM(obj, 7), &node, &nodelen) == -1)
601 if (node_check(PyTuple_GET_ITEM(obj, 7), &node, &nodelen) == -1)
603 return NULL;
602 return NULL;
604
603
605 len = index_length(self);
604 len = index_length(self);
606
605
607 if (offset < 0)
606 if (offset < 0)
608 offset += len;
607 offset += len;
609
608
610 if (offset != len - 1) {
609 if (offset != len - 1) {
611 PyErr_SetString(PyExc_IndexError,
610 PyErr_SetString(PyExc_IndexError,
612 "insert only supported at index -1");
611 "insert only supported at index -1");
613 return NULL;
612 return NULL;
614 }
613 }
615
614
616 if (offset > INT_MAX) {
615 if (offset > INT_MAX) {
617 PyErr_SetString(PyExc_ValueError,
616 PyErr_SetString(PyExc_ValueError,
618 "currently only 2**31 revs supported");
617 "currently only 2**31 revs supported");
619 return NULL;
618 return NULL;
620 }
619 }
621
620
622 if (self->added == NULL) {
621 if (self->added == NULL) {
623 self->added = PyList_New(0);
622 self->added = PyList_New(0);
624 if (self->added == NULL)
623 if (self->added == NULL)
625 return NULL;
624 return NULL;
626 }
625 }
627
626
628 if (PyList_Append(self->added, obj) == -1)
627 if (PyList_Append(self->added, obj) == -1)
629 return NULL;
628 return NULL;
630
629
631 if (self->nt)
630 if (self->nt)
632 nt_insert(self, node, (int)offset);
631 nt_insert(self, node, (int)offset);
633
632
634 Py_CLEAR(self->headrevs);
633 Py_CLEAR(self->headrevs);
635 Py_RETURN_NONE;
634 Py_RETURN_NONE;
636 }
635 }
637
636
638 static void _index_clearcaches(indexObject *self)
637 static void _index_clearcaches(indexObject *self)
639 {
638 {
640 if (self->cache) {
639 if (self->cache) {
641 Py_ssize_t i;
640 Py_ssize_t i;
642
641
643 for (i = 0; i < self->raw_length; i++)
642 for (i = 0; i < self->raw_length; i++)
644 Py_CLEAR(self->cache[i]);
643 Py_CLEAR(self->cache[i]);
645 free(self->cache);
644 free(self->cache);
646 self->cache = NULL;
645 self->cache = NULL;
647 }
646 }
648 if (self->offsets) {
647 if (self->offsets) {
649 free(self->offsets);
648 free(self->offsets);
650 self->offsets = NULL;
649 self->offsets = NULL;
651 }
650 }
652 if (self->nt) {
651 if (self->nt) {
653 free(self->nt);
652 free(self->nt);
654 self->nt = NULL;
653 self->nt = NULL;
655 }
654 }
656 Py_CLEAR(self->headrevs);
655 Py_CLEAR(self->headrevs);
657 }
656 }
658
657
659 static PyObject *index_clearcaches(indexObject *self)
658 static PyObject *index_clearcaches(indexObject *self)
660 {
659 {
661 _index_clearcaches(self);
660 _index_clearcaches(self);
662 self->ntlength = self->ntcapacity = 0;
661 self->ntlength = self->ntcapacity = 0;
663 self->ntdepth = self->ntsplits = 0;
662 self->ntdepth = self->ntsplits = 0;
664 self->ntrev = -1;
663 self->ntrev = -1;
665 self->ntlookups = self->ntmisses = 0;
664 self->ntlookups = self->ntmisses = 0;
666 Py_RETURN_NONE;
665 Py_RETURN_NONE;
667 }
666 }
668
667
669 static PyObject *index_stats(indexObject *self)
668 static PyObject *index_stats(indexObject *self)
670 {
669 {
671 PyObject *obj = PyDict_New();
670 PyObject *obj = PyDict_New();
672
671
673 if (obj == NULL)
672 if (obj == NULL)
674 return NULL;
673 return NULL;
675
674
676 #define istat(__n, __d) \
675 #define istat(__n, __d) \
677 if (PyDict_SetItemString(obj, __d, PyInt_FromSsize_t(self->__n)) == -1) \
676 if (PyDict_SetItemString(obj, __d, PyInt_FromSsize_t(self->__n)) == -1) \
678 goto bail;
677 goto bail;
679
678
680 if (self->added) {
679 if (self->added) {
681 Py_ssize_t len = PyList_GET_SIZE(self->added);
680 Py_ssize_t len = PyList_GET_SIZE(self->added);
682 if (PyDict_SetItemString(obj, "index entries added",
681 if (PyDict_SetItemString(obj, "index entries added",
683 PyInt_FromSsize_t(len)) == -1)
682 PyInt_FromSsize_t(len)) == -1)
684 goto bail;
683 goto bail;
685 }
684 }
686
685
687 if (self->raw_length != self->length - 1)
686 if (self->raw_length != self->length - 1)
688 istat(raw_length, "revs on disk");
687 istat(raw_length, "revs on disk");
689 istat(length, "revs in memory");
688 istat(length, "revs in memory");
690 istat(ntcapacity, "node trie capacity");
689 istat(ntcapacity, "node trie capacity");
691 istat(ntdepth, "node trie depth");
690 istat(ntdepth, "node trie depth");
692 istat(ntlength, "node trie count");
691 istat(ntlength, "node trie count");
693 istat(ntlookups, "node trie lookups");
692 istat(ntlookups, "node trie lookups");
694 istat(ntmisses, "node trie misses");
693 istat(ntmisses, "node trie misses");
695 istat(ntrev, "node trie last rev scanned");
694 istat(ntrev, "node trie last rev scanned");
696 istat(ntsplits, "node trie splits");
695 istat(ntsplits, "node trie splits");
697
696
698 #undef istat
697 #undef istat
699
698
700 return obj;
699 return obj;
701
700
702 bail:
701 bail:
703 Py_XDECREF(obj);
702 Py_XDECREF(obj);
704 return NULL;
703 return NULL;
705 }
704 }
706
705
707 /*
706 /*
708 * When we cache a list, we want to be sure the caller can't mutate
707 * When we cache a list, we want to be sure the caller can't mutate
709 * the cached copy.
708 * the cached copy.
710 */
709 */
711 static PyObject *list_copy(PyObject *list)
710 static PyObject *list_copy(PyObject *list)
712 {
711 {
713 Py_ssize_t len = PyList_GET_SIZE(list);
712 Py_ssize_t len = PyList_GET_SIZE(list);
714 PyObject *newlist = PyList_New(len);
713 PyObject *newlist = PyList_New(len);
715 Py_ssize_t i;
714 Py_ssize_t i;
716
715
717 if (newlist == NULL)
716 if (newlist == NULL)
718 return NULL;
717 return NULL;
719
718
720 for (i = 0; i < len; i++) {
719 for (i = 0; i < len; i++) {
721 PyObject *obj = PyList_GET_ITEM(list, i);
720 PyObject *obj = PyList_GET_ITEM(list, i);
722 Py_INCREF(obj);
721 Py_INCREF(obj);
723 PyList_SET_ITEM(newlist, i, obj);
722 PyList_SET_ITEM(newlist, i, obj);
724 }
723 }
725
724
726 return newlist;
725 return newlist;
727 }
726 }
728
727
729 static PyObject *index_headrevs(indexObject *self)
728 static PyObject *index_headrevs(indexObject *self)
730 {
729 {
731 Py_ssize_t i, len, addlen;
730 Py_ssize_t i, len, addlen;
732 char *nothead = NULL;
731 char *nothead = NULL;
733 PyObject *heads;
732 PyObject *heads;
734
733
735 if (self->headrevs)
734 if (self->headrevs)
736 return list_copy(self->headrevs);
735 return list_copy(self->headrevs);
737
736
738 len = index_length(self) - 1;
737 len = index_length(self) - 1;
739 heads = PyList_New(0);
738 heads = PyList_New(0);
740 if (heads == NULL)
739 if (heads == NULL)
741 goto bail;
740 goto bail;
742 if (len == 0) {
741 if (len == 0) {
743 PyObject *nullid = PyInt_FromLong(-1);
742 PyObject *nullid = PyInt_FromLong(-1);
744 if (nullid == NULL || PyList_Append(heads, nullid) == -1) {
743 if (nullid == NULL || PyList_Append(heads, nullid) == -1) {
745 Py_XDECREF(nullid);
744 Py_XDECREF(nullid);
746 goto bail;
745 goto bail;
747 }
746 }
748 goto done;
747 goto done;
749 }
748 }
750
749
751 nothead = calloc(len, 1);
750 nothead = calloc(len, 1);
752 if (nothead == NULL)
751 if (nothead == NULL)
753 goto bail;
752 goto bail;
754
753
755 for (i = 0; i < self->raw_length; i++) {
754 for (i = 0; i < self->raw_length; i++) {
756 const char *data = index_deref(self, i);
755 const char *data = index_deref(self, i);
757 int parent_1 = getbe32(data + 24);
756 int parent_1 = getbe32(data + 24);
758 int parent_2 = getbe32(data + 28);
757 int parent_2 = getbe32(data + 28);
759 if (parent_1 >= 0)
758 if (parent_1 >= 0)
760 nothead[parent_1] = 1;
759 nothead[parent_1] = 1;
761 if (parent_2 >= 0)
760 if (parent_2 >= 0)
762 nothead[parent_2] = 1;
761 nothead[parent_2] = 1;
763 }
762 }
764
763
765 addlen = self->added ? PyList_GET_SIZE(self->added) : 0;
764 addlen = self->added ? PyList_GET_SIZE(self->added) : 0;
766
765
767 for (i = 0; i < addlen; i++) {
766 for (i = 0; i < addlen; i++) {
768 PyObject *rev = PyList_GET_ITEM(self->added, i);
767 PyObject *rev = PyList_GET_ITEM(self->added, i);
769 PyObject *p1 = PyTuple_GET_ITEM(rev, 5);
768 PyObject *p1 = PyTuple_GET_ITEM(rev, 5);
770 PyObject *p2 = PyTuple_GET_ITEM(rev, 6);
769 PyObject *p2 = PyTuple_GET_ITEM(rev, 6);
771 long parent_1, parent_2;
770 long parent_1, parent_2;
772
771
773 if (!PyInt_Check(p1) || !PyInt_Check(p2)) {
772 if (!PyInt_Check(p1) || !PyInt_Check(p2)) {
774 PyErr_SetString(PyExc_TypeError,
773 PyErr_SetString(PyExc_TypeError,
775 "revlog parents are invalid");
774 "revlog parents are invalid");
776 goto bail;
775 goto bail;
777 }
776 }
778 parent_1 = PyInt_AS_LONG(p1);
777 parent_1 = PyInt_AS_LONG(p1);
779 parent_2 = PyInt_AS_LONG(p2);
778 parent_2 = PyInt_AS_LONG(p2);
780 if (parent_1 >= 0)
779 if (parent_1 >= 0)
781 nothead[parent_1] = 1;
780 nothead[parent_1] = 1;
782 if (parent_2 >= 0)
781 if (parent_2 >= 0)
783 nothead[parent_2] = 1;
782 nothead[parent_2] = 1;
784 }
783 }
785
784
786 for (i = 0; i < len; i++) {
785 for (i = 0; i < len; i++) {
787 PyObject *head;
786 PyObject *head;
788
787
789 if (nothead[i])
788 if (nothead[i])
790 continue;
789 continue;
791 head = PyInt_FromLong(i);
790 head = PyInt_FromLong(i);
792 if (head == NULL || PyList_Append(heads, head) == -1) {
791 if (head == NULL || PyList_Append(heads, head) == -1) {
793 Py_XDECREF(head);
792 Py_XDECREF(head);
794 goto bail;
793 goto bail;
795 }
794 }
796 }
795 }
797
796
798 done:
797 done:
799 self->headrevs = heads;
798 self->headrevs = heads;
800 free(nothead);
799 free(nothead);
801 return list_copy(self->headrevs);
800 return list_copy(self->headrevs);
802 bail:
801 bail:
803 Py_XDECREF(heads);
802 Py_XDECREF(heads);
804 free(nothead);
803 free(nothead);
805 return NULL;
804 return NULL;
806 }
805 }
807
806
808 static inline int nt_level(const char *node, Py_ssize_t level)
807 static inline int nt_level(const char *node, Py_ssize_t level)
809 {
808 {
810 int v = node[level>>1];
809 int v = node[level>>1];
811 if (!(level & 1))
810 if (!(level & 1))
812 v >>= 4;
811 v >>= 4;
813 return v & 0xf;
812 return v & 0xf;
814 }
813 }
815
814
816 /*
815 /*
817 * Return values:
816 * Return values:
818 *
817 *
819 * -4: match is ambiguous (multiple candidates)
818 * -4: match is ambiguous (multiple candidates)
820 * -2: not found
819 * -2: not found
821 * rest: valid rev
820 * rest: valid rev
822 */
821 */
823 static int nt_find(indexObject *self, const char *node, Py_ssize_t nodelen,
822 static int nt_find(indexObject *self, const char *node, Py_ssize_t nodelen,
824 int hex)
823 int hex)
825 {
824 {
826 int (*getnybble)(const char *, Py_ssize_t) = hex ? hexdigit : nt_level;
825 int (*getnybble)(const char *, Py_ssize_t) = hex ? hexdigit : nt_level;
827 int level, maxlevel, off;
826 int level, maxlevel, off;
828
827
829 if (nodelen == 20 && node[0] == '\0' && memcmp(node, nullid, 20) == 0)
828 if (nodelen == 20 && node[0] == '\0' && memcmp(node, nullid, 20) == 0)
830 return -1;
829 return -1;
831
830
832 if (self->nt == NULL)
831 if (self->nt == NULL)
833 return -2;
832 return -2;
834
833
835 if (hex)
834 if (hex)
836 maxlevel = nodelen > 40 ? 40 : (int)nodelen;
835 maxlevel = nodelen > 40 ? 40 : (int)nodelen;
837 else
836 else
838 maxlevel = nodelen > 20 ? 40 : ((int)nodelen * 2);
837 maxlevel = nodelen > 20 ? 40 : ((int)nodelen * 2);
839
838
840 for (level = off = 0; level < maxlevel; level++) {
839 for (level = off = 0; level < maxlevel; level++) {
841 int k = getnybble(node, level);
840 int k = getnybble(node, level);
842 nodetree *n = &self->nt[off];
841 nodetree *n = &self->nt[off];
843 int v = n->children[k];
842 int v = n->children[k];
844
843
845 if (v < 0) {
844 if (v < 0) {
846 const char *n;
845 const char *n;
847 Py_ssize_t i;
846 Py_ssize_t i;
848
847
849 v = -v - 1;
848 v = -v - 1;
850 n = index_node(self, v);
849 n = index_node(self, v);
851 if (n == NULL)
850 if (n == NULL)
852 return -2;
851 return -2;
853 for (i = level; i < maxlevel; i++)
852 for (i = level; i < maxlevel; i++)
854 if (getnybble(node, i) != nt_level(n, i))
853 if (getnybble(node, i) != nt_level(n, i))
855 return -2;
854 return -2;
856 return v;
855 return v;
857 }
856 }
858 if (v == 0)
857 if (v == 0)
859 return -2;
858 return -2;
860 off = v;
859 off = v;
861 }
860 }
862 /* multiple matches against an ambiguous prefix */
861 /* multiple matches against an ambiguous prefix */
863 return -4;
862 return -4;
864 }
863 }
865
864
866 static int nt_new(indexObject *self)
865 static int nt_new(indexObject *self)
867 {
866 {
868 if (self->ntlength == self->ntcapacity) {
867 if (self->ntlength == self->ntcapacity) {
869 self->ntcapacity *= 2;
868 self->ntcapacity *= 2;
870 self->nt = realloc(self->nt,
869 self->nt = realloc(self->nt,
871 self->ntcapacity * sizeof(nodetree));
870 self->ntcapacity * sizeof(nodetree));
872 if (self->nt == NULL) {
871 if (self->nt == NULL) {
873 PyErr_SetString(PyExc_MemoryError, "out of memory");
872 PyErr_SetString(PyExc_MemoryError, "out of memory");
874 return -1;
873 return -1;
875 }
874 }
876 memset(&self->nt[self->ntlength], 0,
875 memset(&self->nt[self->ntlength], 0,
877 sizeof(nodetree) * (self->ntcapacity - self->ntlength));
876 sizeof(nodetree) * (self->ntcapacity - self->ntlength));
878 }
877 }
879 return self->ntlength++;
878 return self->ntlength++;
880 }
879 }
881
880
882 static int nt_insert(indexObject *self, const char *node, int rev)
881 static int nt_insert(indexObject *self, const char *node, int rev)
883 {
882 {
884 int level = 0;
883 int level = 0;
885 int off = 0;
884 int off = 0;
886
885
887 while (level < 40) {
886 while (level < 40) {
888 int k = nt_level(node, level);
887 int k = nt_level(node, level);
889 nodetree *n;
888 nodetree *n;
890 int v;
889 int v;
891
890
892 n = &self->nt[off];
891 n = &self->nt[off];
893 v = n->children[k];
892 v = n->children[k];
894
893
895 if (v == 0) {
894 if (v == 0) {
896 n->children[k] = -rev - 1;
895 n->children[k] = -rev - 1;
897 return 0;
896 return 0;
898 }
897 }
899 if (v < 0) {
898 if (v < 0) {
900 const char *oldnode = index_node(self, -v - 1);
899 const char *oldnode = index_node(self, -v - 1);
901 int noff;
900 int noff;
902
901
903 if (!oldnode || !memcmp(oldnode, node, 20)) {
902 if (!oldnode || !memcmp(oldnode, node, 20)) {
904 n->children[k] = -rev - 1;
903 n->children[k] = -rev - 1;
905 return 0;
904 return 0;
906 }
905 }
907 noff = nt_new(self);
906 noff = nt_new(self);
908 if (noff == -1)
907 if (noff == -1)
909 return -1;
908 return -1;
910 /* self->nt may have been changed by realloc */
909 /* self->nt may have been changed by realloc */
911 self->nt[off].children[k] = noff;
910 self->nt[off].children[k] = noff;
912 off = noff;
911 off = noff;
913 n = &self->nt[off];
912 n = &self->nt[off];
914 n->children[nt_level(oldnode, ++level)] = v;
913 n->children[nt_level(oldnode, ++level)] = v;
915 if (level > self->ntdepth)
914 if (level > self->ntdepth)
916 self->ntdepth = level;
915 self->ntdepth = level;
917 self->ntsplits += 1;
916 self->ntsplits += 1;
918 } else {
917 } else {
919 level += 1;
918 level += 1;
920 off = v;
919 off = v;
921 }
920 }
922 }
921 }
923
922
924 return -1;
923 return -1;
925 }
924 }
926
925
927 static int nt_init(indexObject *self)
926 static int nt_init(indexObject *self)
928 {
927 {
929 if (self->nt == NULL) {
928 if (self->nt == NULL) {
930 if (self->raw_length > INT_MAX) {
929 if (self->raw_length > INT_MAX) {
931 PyErr_SetString(PyExc_ValueError, "overflow in nt_init");
930 PyErr_SetString(PyExc_ValueError, "overflow in nt_init");
932 return -1;
931 return -1;
933 }
932 }
934 self->ntcapacity = self->raw_length < 4
933 self->ntcapacity = self->raw_length < 4
935 ? 4 : (int)self->raw_length / 2;
934 ? 4 : (int)self->raw_length / 2;
936
935
937 self->nt = calloc(self->ntcapacity, sizeof(nodetree));
936 self->nt = calloc(self->ntcapacity, sizeof(nodetree));
938 if (self->nt == NULL) {
937 if (self->nt == NULL) {
939 PyErr_NoMemory();
938 PyErr_NoMemory();
940 return -1;
939 return -1;
941 }
940 }
942 self->ntlength = 1;
941 self->ntlength = 1;
943 self->ntrev = (int)index_length(self) - 1;
942 self->ntrev = (int)index_length(self) - 1;
944 self->ntlookups = 1;
943 self->ntlookups = 1;
945 self->ntmisses = 0;
944 self->ntmisses = 0;
946 if (nt_insert(self, nullid, INT_MAX) == -1)
945 if (nt_insert(self, nullid, INT_MAX) == -1)
947 return -1;
946 return -1;
948 }
947 }
949 return 0;
948 return 0;
950 }
949 }
951
950
952 /*
951 /*
953 * Return values:
952 * Return values:
954 *
953 *
955 * -3: error (exception set)
954 * -3: error (exception set)
956 * -2: not found (no exception set)
955 * -2: not found (no exception set)
957 * rest: valid rev
956 * rest: valid rev
958 */
957 */
959 static int index_find_node(indexObject *self,
958 static int index_find_node(indexObject *self,
960 const char *node, Py_ssize_t nodelen)
959 const char *node, Py_ssize_t nodelen)
961 {
960 {
962 int rev;
961 int rev;
963
962
964 self->ntlookups++;
963 self->ntlookups++;
965 rev = nt_find(self, node, nodelen, 0);
964 rev = nt_find(self, node, nodelen, 0);
966 if (rev >= -1)
965 if (rev >= -1)
967 return rev;
966 return rev;
968
967
969 if (nt_init(self) == -1)
968 if (nt_init(self) == -1)
970 return -3;
969 return -3;
971
970
972 /*
971 /*
973 * For the first handful of lookups, we scan the entire index,
972 * For the first handful of lookups, we scan the entire index,
974 * and cache only the matching nodes. This optimizes for cases
973 * and cache only the matching nodes. This optimizes for cases
975 * like "hg tip", where only a few nodes are accessed.
974 * like "hg tip", where only a few nodes are accessed.
976 *
975 *
977 * After that, we cache every node we visit, using a single
976 * After that, we cache every node we visit, using a single
978 * scan amortized over multiple lookups. This gives the best
977 * scan amortized over multiple lookups. This gives the best
979 * bulk performance, e.g. for "hg log".
978 * bulk performance, e.g. for "hg log".
980 */
979 */
981 if (self->ntmisses++ < 4) {
980 if (self->ntmisses++ < 4) {
982 for (rev = self->ntrev - 1; rev >= 0; rev--) {
981 for (rev = self->ntrev - 1; rev >= 0; rev--) {
983 const char *n = index_node(self, rev);
982 const char *n = index_node(self, rev);
984 if (n == NULL)
983 if (n == NULL)
985 return -2;
984 return -2;
986 if (memcmp(node, n, nodelen > 20 ? 20 : nodelen) == 0) {
985 if (memcmp(node, n, nodelen > 20 ? 20 : nodelen) == 0) {
987 if (nt_insert(self, n, rev) == -1)
986 if (nt_insert(self, n, rev) == -1)
988 return -3;
987 return -3;
989 break;
988 break;
990 }
989 }
991 }
990 }
992 } else {
991 } else {
993 for (rev = self->ntrev - 1; rev >= 0; rev--) {
992 for (rev = self->ntrev - 1; rev >= 0; rev--) {
994 const char *n = index_node(self, rev);
993 const char *n = index_node(self, rev);
995 if (n == NULL) {
994 if (n == NULL) {
996 self->ntrev = rev + 1;
995 self->ntrev = rev + 1;
997 return -2;
996 return -2;
998 }
997 }
999 if (nt_insert(self, n, rev) == -1) {
998 if (nt_insert(self, n, rev) == -1) {
1000 self->ntrev = rev + 1;
999 self->ntrev = rev + 1;
1001 return -3;
1000 return -3;
1002 }
1001 }
1003 if (memcmp(node, n, nodelen > 20 ? 20 : nodelen) == 0) {
1002 if (memcmp(node, n, nodelen > 20 ? 20 : nodelen) == 0) {
1004 break;
1003 break;
1005 }
1004 }
1006 }
1005 }
1007 self->ntrev = rev;
1006 self->ntrev = rev;
1008 }
1007 }
1009
1008
1010 if (rev >= 0)
1009 if (rev >= 0)
1011 return rev;
1010 return rev;
1012 return -2;
1011 return -2;
1013 }
1012 }
1014
1013
1015 static PyObject *raise_revlog_error(void)
1014 static PyObject *raise_revlog_error(void)
1016 {
1015 {
1017 static PyObject *errclass;
1016 static PyObject *errclass;
1018 PyObject *mod = NULL, *errobj;
1017 PyObject *mod = NULL, *errobj;
1019
1018
1020 if (errclass == NULL) {
1019 if (errclass == NULL) {
1021 PyObject *dict;
1020 PyObject *dict;
1022
1021
1023 mod = PyImport_ImportModule("mercurial.error");
1022 mod = PyImport_ImportModule("mercurial.error");
1024 if (mod == NULL)
1023 if (mod == NULL)
1025 goto classfail;
1024 goto classfail;
1026
1025
1027 dict = PyModule_GetDict(mod);
1026 dict = PyModule_GetDict(mod);
1028 if (dict == NULL)
1027 if (dict == NULL)
1029 goto classfail;
1028 goto classfail;
1030
1029
1031 errclass = PyDict_GetItemString(dict, "RevlogError");
1030 errclass = PyDict_GetItemString(dict, "RevlogError");
1032 if (errclass == NULL) {
1031 if (errclass == NULL) {
1033 PyErr_SetString(PyExc_SystemError,
1032 PyErr_SetString(PyExc_SystemError,
1034 "could not find RevlogError");
1033 "could not find RevlogError");
1035 goto classfail;
1034 goto classfail;
1036 }
1035 }
1037 Py_INCREF(errclass);
1036 Py_INCREF(errclass);
1038 }
1037 }
1039
1038
1040 errobj = PyObject_CallFunction(errclass, NULL);
1039 errobj = PyObject_CallFunction(errclass, NULL);
1041 if (errobj == NULL)
1040 if (errobj == NULL)
1042 return NULL;
1041 return NULL;
1043 PyErr_SetObject(errclass, errobj);
1042 PyErr_SetObject(errclass, errobj);
1044 return errobj;
1043 return errobj;
1045
1044
1046 classfail:
1045 classfail:
1047 Py_XDECREF(mod);
1046 Py_XDECREF(mod);
1048 return NULL;
1047 return NULL;
1049 }
1048 }
1050
1049
1051 static PyObject *index_getitem(indexObject *self, PyObject *value)
1050 static PyObject *index_getitem(indexObject *self, PyObject *value)
1052 {
1051 {
1053 char *node;
1052 char *node;
1054 Py_ssize_t nodelen;
1053 Py_ssize_t nodelen;
1055 int rev;
1054 int rev;
1056
1055
1057 if (PyInt_Check(value))
1056 if (PyInt_Check(value))
1058 return index_get(self, PyInt_AS_LONG(value));
1057 return index_get(self, PyInt_AS_LONG(value));
1059
1058
1060 if (node_check(value, &node, &nodelen) == -1)
1059 if (node_check(value, &node, &nodelen) == -1)
1061 return NULL;
1060 return NULL;
1062 rev = index_find_node(self, node, nodelen);
1061 rev = index_find_node(self, node, nodelen);
1063 if (rev >= -1)
1062 if (rev >= -1)
1064 return PyInt_FromLong(rev);
1063 return PyInt_FromLong(rev);
1065 if (rev == -2)
1064 if (rev == -2)
1066 raise_revlog_error();
1065 raise_revlog_error();
1067 return NULL;
1066 return NULL;
1068 }
1067 }
1069
1068
1070 static int nt_partialmatch(indexObject *self, const char *node,
1069 static int nt_partialmatch(indexObject *self, const char *node,
1071 Py_ssize_t nodelen)
1070 Py_ssize_t nodelen)
1072 {
1071 {
1073 int rev;
1072 int rev;
1074
1073
1075 if (nt_init(self) == -1)
1074 if (nt_init(self) == -1)
1076 return -3;
1075 return -3;
1077
1076
1078 if (self->ntrev > 0) {
1077 if (self->ntrev > 0) {
1079 /* ensure that the radix tree is fully populated */
1078 /* ensure that the radix tree is fully populated */
1080 for (rev = self->ntrev - 1; rev >= 0; rev--) {
1079 for (rev = self->ntrev - 1; rev >= 0; rev--) {
1081 const char *n = index_node(self, rev);
1080 const char *n = index_node(self, rev);
1082 if (n == NULL)
1081 if (n == NULL)
1083 return -2;
1082 return -2;
1084 if (nt_insert(self, n, rev) == -1)
1083 if (nt_insert(self, n, rev) == -1)
1085 return -3;
1084 return -3;
1086 }
1085 }
1087 self->ntrev = rev;
1086 self->ntrev = rev;
1088 }
1087 }
1089
1088
1090 return nt_find(self, node, nodelen, 1);
1089 return nt_find(self, node, nodelen, 1);
1091 }
1090 }
1092
1091
1093 static PyObject *index_partialmatch(indexObject *self, PyObject *args)
1092 static PyObject *index_partialmatch(indexObject *self, PyObject *args)
1094 {
1093 {
1095 const char *fullnode;
1094 const char *fullnode;
1096 int nodelen;
1095 int nodelen;
1097 char *node;
1096 char *node;
1098 int rev, i;
1097 int rev, i;
1099
1098
1100 if (!PyArg_ParseTuple(args, "s#", &node, &nodelen))
1099 if (!PyArg_ParseTuple(args, "s#", &node, &nodelen))
1101 return NULL;
1100 return NULL;
1102
1101
1103 if (nodelen < 4) {
1102 if (nodelen < 4) {
1104 PyErr_SetString(PyExc_ValueError, "key too short");
1103 PyErr_SetString(PyExc_ValueError, "key too short");
1105 return NULL;
1104 return NULL;
1106 }
1105 }
1107
1106
1108 if (nodelen > 40) {
1107 if (nodelen > 40) {
1109 PyErr_SetString(PyExc_ValueError, "key too long");
1108 PyErr_SetString(PyExc_ValueError, "key too long");
1110 return NULL;
1109 return NULL;
1111 }
1110 }
1112
1111
1113 for (i = 0; i < nodelen; i++)
1112 for (i = 0; i < nodelen; i++)
1114 hexdigit(node, i);
1113 hexdigit(node, i);
1115 if (PyErr_Occurred()) {
1114 if (PyErr_Occurred()) {
1116 /* input contains non-hex characters */
1115 /* input contains non-hex characters */
1117 PyErr_Clear();
1116 PyErr_Clear();
1118 Py_RETURN_NONE;
1117 Py_RETURN_NONE;
1119 }
1118 }
1120
1119
1121 rev = nt_partialmatch(self, node, nodelen);
1120 rev = nt_partialmatch(self, node, nodelen);
1122
1121
1123 switch (rev) {
1122 switch (rev) {
1124 case -4:
1123 case -4:
1125 raise_revlog_error();
1124 raise_revlog_error();
1126 case -3:
1125 case -3:
1127 return NULL;
1126 return NULL;
1128 case -2:
1127 case -2:
1129 Py_RETURN_NONE;
1128 Py_RETURN_NONE;
1130 case -1:
1129 case -1:
1131 return PyString_FromStringAndSize(nullid, 20);
1130 return PyString_FromStringAndSize(nullid, 20);
1132 }
1131 }
1133
1132
1134 fullnode = index_node(self, rev);
1133 fullnode = index_node(self, rev);
1135 if (fullnode == NULL) {
1134 if (fullnode == NULL) {
1136 PyErr_Format(PyExc_IndexError,
1135 PyErr_Format(PyExc_IndexError,
1137 "could not access rev %d", rev);
1136 "could not access rev %d", rev);
1138 return NULL;
1137 return NULL;
1139 }
1138 }
1140 return PyString_FromStringAndSize(fullnode, 20);
1139 return PyString_FromStringAndSize(fullnode, 20);
1141 }
1140 }
1142
1141
1143 static PyObject *index_m_get(indexObject *self, PyObject *args)
1142 static PyObject *index_m_get(indexObject *self, PyObject *args)
1144 {
1143 {
1145 Py_ssize_t nodelen;
1144 Py_ssize_t nodelen;
1146 PyObject *val;
1145 PyObject *val;
1147 char *node;
1146 char *node;
1148 int rev;
1147 int rev;
1149
1148
1150 if (!PyArg_ParseTuple(args, "O", &val))
1149 if (!PyArg_ParseTuple(args, "O", &val))
1151 return NULL;
1150 return NULL;
1152 if (node_check(val, &node, &nodelen) == -1)
1151 if (node_check(val, &node, &nodelen) == -1)
1153 return NULL;
1152 return NULL;
1154 rev = index_find_node(self, node, nodelen);
1153 rev = index_find_node(self, node, nodelen);
1155 if (rev == -3)
1154 if (rev == -3)
1156 return NULL;
1155 return NULL;
1157 if (rev == -2)
1156 if (rev == -2)
1158 Py_RETURN_NONE;
1157 Py_RETURN_NONE;
1159 return PyInt_FromLong(rev);
1158 return PyInt_FromLong(rev);
1160 }
1159 }
1161
1160
1162 static int index_contains(indexObject *self, PyObject *value)
1161 static int index_contains(indexObject *self, PyObject *value)
1163 {
1162 {
1164 char *node;
1163 char *node;
1165 Py_ssize_t nodelen;
1164 Py_ssize_t nodelen;
1166
1165
1167 if (PyInt_Check(value)) {
1166 if (PyInt_Check(value)) {
1168 long rev = PyInt_AS_LONG(value);
1167 long rev = PyInt_AS_LONG(value);
1169 return rev >= -1 && rev < index_length(self);
1168 return rev >= -1 && rev < index_length(self);
1170 }
1169 }
1171
1170
1172 if (node_check(value, &node, &nodelen) == -1)
1171 if (node_check(value, &node, &nodelen) == -1)
1173 return -1;
1172 return -1;
1174
1173
1175 switch (index_find_node(self, node, nodelen)) {
1174 switch (index_find_node(self, node, nodelen)) {
1176 case -3:
1175 case -3:
1177 return -1;
1176 return -1;
1178 case -2:
1177 case -2:
1179 return 0;
1178 return 0;
1180 default:
1179 default:
1181 return 1;
1180 return 1;
1182 }
1181 }
1183 }
1182 }
1184
1183
1185 static inline void index_get_parents(indexObject *self, int rev, int *ps)
1184 static inline void index_get_parents(indexObject *self, int rev, int *ps)
1186 {
1185 {
1187 if (rev >= self->length - 1) {
1186 if (rev >= self->length - 1) {
1188 PyObject *tuple = PyList_GET_ITEM(self->added,
1187 PyObject *tuple = PyList_GET_ITEM(self->added,
1189 rev - self->length + 1);
1188 rev - self->length + 1);
1190 ps[0] = (int)PyInt_AS_LONG(PyTuple_GET_ITEM(tuple, 5));
1189 ps[0] = (int)PyInt_AS_LONG(PyTuple_GET_ITEM(tuple, 5));
1191 ps[1] = (int)PyInt_AS_LONG(PyTuple_GET_ITEM(tuple, 6));
1190 ps[1] = (int)PyInt_AS_LONG(PyTuple_GET_ITEM(tuple, 6));
1192 } else {
1191 } else {
1193 const char *data = index_deref(self, rev);
1192 const char *data = index_deref(self, rev);
1194 ps[0] = getbe32(data + 24);
1193 ps[0] = getbe32(data + 24);
1195 ps[1] = getbe32(data + 28);
1194 ps[1] = getbe32(data + 28);
1196 }
1195 }
1197 }
1196 }
1198
1197
1199 typedef uint64_t bitmask;
1198 typedef uint64_t bitmask;
1200
1199
1201 /*
1200 /*
1202 * Given a disjoint set of revs, return all candidates for the
1201 * Given a disjoint set of revs, return all candidates for the
1203 * greatest common ancestor. In revset notation, this is the set
1202 * greatest common ancestor. In revset notation, this is the set
1204 * "heads(::a and ::b and ...)"
1203 * "heads(::a and ::b and ...)"
1205 */
1204 */
1206 static PyObject *find_gca_candidates(indexObject *self, const int *revs,
1205 static PyObject *find_gca_candidates(indexObject *self, const int *revs,
1207 int revcount)
1206 int revcount)
1208 {
1207 {
1209 const bitmask allseen = (1ull << revcount) - 1;
1208 const bitmask allseen = (1ull << revcount) - 1;
1210 const bitmask poison = 1ull << revcount;
1209 const bitmask poison = 1ull << revcount;
1211 PyObject *gca = PyList_New(0);
1210 PyObject *gca = PyList_New(0);
1212 int i, v, interesting, left;
1211 int i, v, interesting, left;
1213 int maxrev = -1;
1212 int maxrev = -1;
1214 long sp;
1213 long sp;
1215 bitmask *seen;
1214 bitmask *seen;
1216
1215
1217 if (gca == NULL)
1216 if (gca == NULL)
1218 return PyErr_NoMemory();
1217 return PyErr_NoMemory();
1219
1218
1220 for (i = 0; i < revcount; i++) {
1219 for (i = 0; i < revcount; i++) {
1221 if (revs[i] > maxrev)
1220 if (revs[i] > maxrev)
1222 maxrev = revs[i];
1221 maxrev = revs[i];
1223 }
1222 }
1224
1223
1225 seen = calloc(sizeof(*seen), maxrev + 1);
1224 seen = calloc(sizeof(*seen), maxrev + 1);
1226 if (seen == NULL) {
1225 if (seen == NULL) {
1227 Py_DECREF(gca);
1226 Py_DECREF(gca);
1228 return PyErr_NoMemory();
1227 return PyErr_NoMemory();
1229 }
1228 }
1230
1229
1231 for (i = 0; i < revcount; i++)
1230 for (i = 0; i < revcount; i++)
1232 seen[revs[i]] = 1ull << i;
1231 seen[revs[i]] = 1ull << i;
1233
1232
1234 interesting = left = revcount;
1233 interesting = left = revcount;
1235
1234
1236 for (v = maxrev; v >= 0 && interesting; v--) {
1235 for (v = maxrev; v >= 0 && interesting; v--) {
1237 long sv = seen[v];
1236 long sv = seen[v];
1238 int parents[2];
1237 int parents[2];
1239
1238
1240 if (!sv)
1239 if (!sv)
1241 continue;
1240 continue;
1242
1241
1243 if (sv < poison) {
1242 if (sv < poison) {
1244 interesting -= 1;
1243 interesting -= 1;
1245 if (sv == allseen) {
1244 if (sv == allseen) {
1246 PyObject *obj = PyInt_FromLong(v);
1245 PyObject *obj = PyInt_FromLong(v);
1247 if (obj == NULL)
1246 if (obj == NULL)
1248 goto bail;
1247 goto bail;
1249 if (PyList_Append(gca, obj) == -1) {
1248 if (PyList_Append(gca, obj) == -1) {
1250 Py_DECREF(obj);
1249 Py_DECREF(obj);
1251 goto bail;
1250 goto bail;
1252 }
1251 }
1253 sv |= poison;
1252 sv |= poison;
1254 for (i = 0; i < revcount; i++) {
1253 for (i = 0; i < revcount; i++) {
1255 if (revs[i] == v) {
1254 if (revs[i] == v) {
1256 if (--left <= 1)
1255 if (--left <= 1)
1257 goto done;
1256 goto done;
1258 break;
1257 break;
1259 }
1258 }
1260 }
1259 }
1261 }
1260 }
1262 }
1261 }
1263 index_get_parents(self, v, parents);
1262 index_get_parents(self, v, parents);
1264
1263
1265 for (i = 0; i < 2; i++) {
1264 for (i = 0; i < 2; i++) {
1266 int p = parents[i];
1265 int p = parents[i];
1267 if (p == -1)
1266 if (p == -1)
1268 continue;
1267 continue;
1269 sp = seen[p];
1268 sp = seen[p];
1270 if (sv < poison) {
1269 if (sv < poison) {
1271 if (sp == 0) {
1270 if (sp == 0) {
1272 seen[p] = sv;
1271 seen[p] = sv;
1273 interesting++;
1272 interesting++;
1274 }
1273 }
1275 else if (sp != sv)
1274 else if (sp != sv)
1276 seen[p] |= sv;
1275 seen[p] |= sv;
1277 } else {
1276 } else {
1278 if (sp && sp < poison)
1277 if (sp && sp < poison)
1279 interesting--;
1278 interesting--;
1280 seen[p] = sv;
1279 seen[p] = sv;
1281 }
1280 }
1282 }
1281 }
1283 }
1282 }
1284
1283
1285 done:
1284 done:
1286 free(seen);
1285 free(seen);
1287 return gca;
1286 return gca;
1288 bail:
1287 bail:
1289 free(seen);
1288 free(seen);
1290 Py_XDECREF(gca);
1289 Py_XDECREF(gca);
1291 return NULL;
1290 return NULL;
1292 }
1291 }
1293
1292
1294 /*
1293 /*
1295 * Given a disjoint set of revs, return the subset with the longest
1294 * Given a disjoint set of revs, return the subset with the longest
1296 * path to the root.
1295 * path to the root.
1297 */
1296 */
1298 static PyObject *find_deepest(indexObject *self, PyObject *revs)
1297 static PyObject *find_deepest(indexObject *self, PyObject *revs)
1299 {
1298 {
1300 const Py_ssize_t revcount = PyList_GET_SIZE(revs);
1299 const Py_ssize_t revcount = PyList_GET_SIZE(revs);
1301 static const Py_ssize_t capacity = 24;
1300 static const Py_ssize_t capacity = 24;
1302 int *depth, *interesting = NULL;
1301 int *depth, *interesting = NULL;
1303 int i, j, v, ninteresting;
1302 int i, j, v, ninteresting;
1304 PyObject *dict = NULL, *keys;
1303 PyObject *dict = NULL, *keys;
1305 long *seen = NULL;
1304 long *seen = NULL;
1306 int maxrev = -1;
1305 int maxrev = -1;
1307 long final;
1306 long final;
1308
1307
1309 if (revcount > capacity) {
1308 if (revcount > capacity) {
1310 PyErr_Format(PyExc_OverflowError,
1309 PyErr_Format(PyExc_OverflowError,
1311 "bitset size (%ld) > capacity (%ld)",
1310 "bitset size (%ld) > capacity (%ld)",
1312 (long)revcount, (long)capacity);
1311 (long)revcount, (long)capacity);
1313 return NULL;
1312 return NULL;
1314 }
1313 }
1315
1314
1316 for (i = 0; i < revcount; i++) {
1315 for (i = 0; i < revcount; i++) {
1317 int n = (int)PyInt_AsLong(PyList_GET_ITEM(revs, i));
1316 int n = (int)PyInt_AsLong(PyList_GET_ITEM(revs, i));
1318 if (n > maxrev)
1317 if (n > maxrev)
1319 maxrev = n;
1318 maxrev = n;
1320 }
1319 }
1321
1320
1322 depth = calloc(sizeof(*depth), maxrev + 1);
1321 depth = calloc(sizeof(*depth), maxrev + 1);
1323 if (depth == NULL)
1322 if (depth == NULL)
1324 return PyErr_NoMemory();
1323 return PyErr_NoMemory();
1325
1324
1326 seen = calloc(sizeof(*seen), maxrev + 1);
1325 seen = calloc(sizeof(*seen), maxrev + 1);
1327 if (seen == NULL) {
1326 if (seen == NULL) {
1328 PyErr_NoMemory();
1327 PyErr_NoMemory();
1329 goto bail;
1328 goto bail;
1330 }
1329 }
1331
1330
1332 interesting = calloc(sizeof(*interesting), 2 << revcount);
1331 interesting = calloc(sizeof(*interesting), 2 << revcount);
1333 if (interesting == NULL) {
1332 if (interesting == NULL) {
1334 PyErr_NoMemory();
1333 PyErr_NoMemory();
1335 goto bail;
1334 goto bail;
1336 }
1335 }
1337
1336
1338 if (PyList_Sort(revs) == -1)
1337 if (PyList_Sort(revs) == -1)
1339 goto bail;
1338 goto bail;
1340
1339
1341 for (i = 0; i < revcount; i++) {
1340 for (i = 0; i < revcount; i++) {
1342 int n = (int)PyInt_AsLong(PyList_GET_ITEM(revs, i));
1341 int n = (int)PyInt_AsLong(PyList_GET_ITEM(revs, i));
1343 long b = 1l << i;
1342 long b = 1l << i;
1344 depth[n] = 1;
1343 depth[n] = 1;
1345 seen[n] = b;
1344 seen[n] = b;
1346 interesting[b] = 1;
1345 interesting[b] = 1;
1347 }
1346 }
1348
1347
1349 ninteresting = (int)revcount;
1348 ninteresting = (int)revcount;
1350
1349
1351 for (v = maxrev; v >= 0 && ninteresting > 1; v--) {
1350 for (v = maxrev; v >= 0 && ninteresting > 1; v--) {
1352 int dv = depth[v];
1351 int dv = depth[v];
1353 int parents[2];
1352 int parents[2];
1354 long sv;
1353 long sv;
1355
1354
1356 if (dv == 0)
1355 if (dv == 0)
1357 continue;
1356 continue;
1358
1357
1359 sv = seen[v];
1358 sv = seen[v];
1360 index_get_parents(self, v, parents);
1359 index_get_parents(self, v, parents);
1361
1360
1362 for (i = 0; i < 2; i++) {
1361 for (i = 0; i < 2; i++) {
1363 int p = parents[i];
1362 int p = parents[i];
1364 long nsp, sp;
1363 long nsp, sp;
1365 int dp;
1364 int dp;
1366
1365
1367 if (p == -1)
1366 if (p == -1)
1368 continue;
1367 continue;
1369
1368
1370 dp = depth[p];
1369 dp = depth[p];
1371 nsp = sp = seen[p];
1370 nsp = sp = seen[p];
1372 if (dp <= dv) {
1371 if (dp <= dv) {
1373 depth[p] = dv + 1;
1372 depth[p] = dv + 1;
1374 if (sp != sv) {
1373 if (sp != sv) {
1375 interesting[sv] += 1;
1374 interesting[sv] += 1;
1376 nsp = seen[p] = sv;
1375 nsp = seen[p] = sv;
1377 if (sp) {
1376 if (sp) {
1378 interesting[sp] -= 1;
1377 interesting[sp] -= 1;
1379 if (interesting[sp] == 0)
1378 if (interesting[sp] == 0)
1380 ninteresting -= 1;
1379 ninteresting -= 1;
1381 }
1380 }
1382 }
1381 }
1383 }
1382 }
1384 else if (dv == dp - 1) {
1383 else if (dv == dp - 1) {
1385 nsp = sp | sv;
1384 nsp = sp | sv;
1386 if (nsp == sp)
1385 if (nsp == sp)
1387 continue;
1386 continue;
1388 seen[p] = nsp;
1387 seen[p] = nsp;
1389 interesting[sp] -= 1;
1388 interesting[sp] -= 1;
1390 if (interesting[sp] == 0 && interesting[nsp] > 0)
1389 if (interesting[sp] == 0 && interesting[nsp] > 0)
1391 ninteresting -= 1;
1390 ninteresting -= 1;
1392 interesting[nsp] += 1;
1391 interesting[nsp] += 1;
1393 }
1392 }
1394 }
1393 }
1395 interesting[sv] -= 1;
1394 interesting[sv] -= 1;
1396 if (interesting[sv] == 0)
1395 if (interesting[sv] == 0)
1397 ninteresting -= 1;
1396 ninteresting -= 1;
1398 }
1397 }
1399
1398
1400 final = 0;
1399 final = 0;
1401 j = ninteresting;
1400 j = ninteresting;
1402 for (i = 0; i < (int)(2 << revcount) && j > 0; i++) {
1401 for (i = 0; i < (int)(2 << revcount) && j > 0; i++) {
1403 if (interesting[i] == 0)
1402 if (interesting[i] == 0)
1404 continue;
1403 continue;
1405 final |= i;
1404 final |= i;
1406 j -= 1;
1405 j -= 1;
1407 }
1406 }
1408 if (final == 0)
1407 if (final == 0)
1409 return PyList_New(0);
1408 return PyList_New(0);
1410
1409
1411 dict = PyDict_New();
1410 dict = PyDict_New();
1412 if (dict == NULL)
1411 if (dict == NULL)
1413 goto bail;
1412 goto bail;
1414
1413
1415 for (i = 0; i < revcount; i++) {
1414 for (i = 0; i < revcount; i++) {
1416 PyObject *key;
1415 PyObject *key;
1417
1416
1418 if ((final & (1 << i)) == 0)
1417 if ((final & (1 << i)) == 0)
1419 continue;
1418 continue;
1420
1419
1421 key = PyList_GET_ITEM(revs, i);
1420 key = PyList_GET_ITEM(revs, i);
1422 Py_INCREF(key);
1421 Py_INCREF(key);
1423 Py_INCREF(Py_None);
1422 Py_INCREF(Py_None);
1424 if (PyDict_SetItem(dict, key, Py_None) == -1) {
1423 if (PyDict_SetItem(dict, key, Py_None) == -1) {
1425 Py_DECREF(key);
1424 Py_DECREF(key);
1426 Py_DECREF(Py_None);
1425 Py_DECREF(Py_None);
1427 goto bail;
1426 goto bail;
1428 }
1427 }
1429 }
1428 }
1430
1429
1431 keys = PyDict_Keys(dict);
1430 keys = PyDict_Keys(dict);
1432
1431
1433 free(depth);
1432 free(depth);
1434 free(seen);
1433 free(seen);
1435 free(interesting);
1434 free(interesting);
1436 Py_DECREF(dict);
1435 Py_DECREF(dict);
1437
1436
1438 return keys;
1437 return keys;
1439 bail:
1438 bail:
1440 free(depth);
1439 free(depth);
1441 free(seen);
1440 free(seen);
1442 free(interesting);
1441 free(interesting);
1443 Py_XDECREF(dict);
1442 Py_XDECREF(dict);
1444
1443
1445 return NULL;
1444 return NULL;
1446 }
1445 }
1447
1446
1448 /*
1447 /*
1449 * Given a (possibly overlapping) set of revs, return the greatest
1448 * Given a (possibly overlapping) set of revs, return the greatest
1450 * common ancestors: those with the longest path to the root.
1449 * common ancestors: those with the longest path to the root.
1451 */
1450 */
1452 static PyObject *index_ancestors(indexObject *self, PyObject *args)
1451 static PyObject *index_ancestors(indexObject *self, PyObject *args)
1453 {
1452 {
1454 PyObject *ret = NULL, *gca = NULL;
1453 PyObject *ret = NULL, *gca = NULL;
1455 Py_ssize_t argcount, i, len;
1454 Py_ssize_t argcount, i, len;
1456 bitmask repeat = 0;
1455 bitmask repeat = 0;
1457 int revcount = 0;
1456 int revcount = 0;
1458 int *revs;
1457 int *revs;
1459
1458
1460 argcount = PySequence_Length(args);
1459 argcount = PySequence_Length(args);
1461 revs = malloc(argcount * sizeof(*revs));
1460 revs = malloc(argcount * sizeof(*revs));
1462 if (argcount > 0 && revs == NULL)
1461 if (argcount > 0 && revs == NULL)
1463 return PyErr_NoMemory();
1462 return PyErr_NoMemory();
1464 len = index_length(self) - 1;
1463 len = index_length(self) - 1;
1465
1464
1466 for (i = 0; i < argcount; i++) {
1465 for (i = 0; i < argcount; i++) {
1467 static const int capacity = 24;
1466 static const int capacity = 24;
1468 PyObject *obj = PySequence_GetItem(args, i);
1467 PyObject *obj = PySequence_GetItem(args, i);
1469 bitmask x;
1468 bitmask x;
1470 long val;
1469 long val;
1471
1470
1472 if (!PyInt_Check(obj)) {
1471 if (!PyInt_Check(obj)) {
1473 PyErr_SetString(PyExc_TypeError,
1472 PyErr_SetString(PyExc_TypeError,
1474 "arguments must all be ints");
1473 "arguments must all be ints");
1475 goto bail;
1474 goto bail;
1476 }
1475 }
1477 val = PyInt_AsLong(obj);
1476 val = PyInt_AsLong(obj);
1478 if (val == -1) {
1477 if (val == -1) {
1479 ret = PyList_New(0);
1478 ret = PyList_New(0);
1480 goto done;
1479 goto done;
1481 }
1480 }
1482 if (val < 0 || val >= len) {
1481 if (val < 0 || val >= len) {
1483 PyErr_SetString(PyExc_IndexError,
1482 PyErr_SetString(PyExc_IndexError,
1484 "index out of range");
1483 "index out of range");
1485 goto bail;
1484 goto bail;
1486 }
1485 }
1487 /* this cheesy bloom filter lets us avoid some more
1486 /* this cheesy bloom filter lets us avoid some more
1488 * expensive duplicate checks in the common set-is-disjoint
1487 * expensive duplicate checks in the common set-is-disjoint
1489 * case */
1488 * case */
1490 x = 1ull << (val & 0x3f);
1489 x = 1ull << (val & 0x3f);
1491 if (repeat & x) {
1490 if (repeat & x) {
1492 int k;
1491 int k;
1493 for (k = 0; k < revcount; k++) {
1492 for (k = 0; k < revcount; k++) {
1494 if (val == revs[k])
1493 if (val == revs[k])
1495 goto duplicate;
1494 goto duplicate;
1496 }
1495 }
1497 }
1496 }
1498 else repeat |= x;
1497 else repeat |= x;
1499 if (revcount >= capacity) {
1498 if (revcount >= capacity) {
1500 PyErr_Format(PyExc_OverflowError,
1499 PyErr_Format(PyExc_OverflowError,
1501 "bitset size (%d) > capacity (%d)",
1500 "bitset size (%d) > capacity (%d)",
1502 revcount, capacity);
1501 revcount, capacity);
1503 goto bail;
1502 goto bail;
1504 }
1503 }
1505 revs[revcount++] = (int)val;
1504 revs[revcount++] = (int)val;
1506 duplicate:;
1505 duplicate:;
1507 }
1506 }
1508
1507
1509 if (revcount == 0) {
1508 if (revcount == 0) {
1510 ret = PyList_New(0);
1509 ret = PyList_New(0);
1511 goto done;
1510 goto done;
1512 }
1511 }
1513 if (revcount == 1) {
1512 if (revcount == 1) {
1514 PyObject *obj;
1513 PyObject *obj;
1515 ret = PyList_New(1);
1514 ret = PyList_New(1);
1516 if (ret == NULL)
1515 if (ret == NULL)
1517 goto bail;
1516 goto bail;
1518 obj = PyInt_FromLong(revs[0]);
1517 obj = PyInt_FromLong(revs[0]);
1519 if (obj == NULL)
1518 if (obj == NULL)
1520 goto bail;
1519 goto bail;
1521 PyList_SET_ITEM(ret, 0, obj);
1520 PyList_SET_ITEM(ret, 0, obj);
1522 goto done;
1521 goto done;
1523 }
1522 }
1524
1523
1525 gca = find_gca_candidates(self, revs, revcount);
1524 gca = find_gca_candidates(self, revs, revcount);
1526 if (gca == NULL)
1525 if (gca == NULL)
1527 goto bail;
1526 goto bail;
1528
1527
1529 if (PyList_GET_SIZE(gca) <= 1) {
1528 if (PyList_GET_SIZE(gca) <= 1) {
1530 ret = gca;
1529 ret = gca;
1531 Py_INCREF(gca);
1530 Py_INCREF(gca);
1532 }
1531 }
1533 else if (PyList_GET_SIZE(gca) == 1) {
1532 else if (PyList_GET_SIZE(gca) == 1) {
1534 ret = PyList_GET_ITEM(gca, 0);
1533 ret = PyList_GET_ITEM(gca, 0);
1535 Py_INCREF(ret);
1534 Py_INCREF(ret);
1536 }
1535 }
1537 else ret = find_deepest(self, gca);
1536 else ret = find_deepest(self, gca);
1538
1537
1539 done:
1538 done:
1540 free(revs);
1539 free(revs);
1541 Py_XDECREF(gca);
1540 Py_XDECREF(gca);
1542
1541
1543 return ret;
1542 return ret;
1544
1543
1545 bail:
1544 bail:
1546 free(revs);
1545 free(revs);
1547 Py_XDECREF(gca);
1546 Py_XDECREF(gca);
1548 Py_XDECREF(ret);
1547 Py_XDECREF(ret);
1549 return NULL;
1548 return NULL;
1550 }
1549 }
1551
1550
1552 /*
1551 /*
1553 * Invalidate any trie entries introduced by added revs.
1552 * Invalidate any trie entries introduced by added revs.
1554 */
1553 */
1555 static void nt_invalidate_added(indexObject *self, Py_ssize_t start)
1554 static void nt_invalidate_added(indexObject *self, Py_ssize_t start)
1556 {
1555 {
1557 Py_ssize_t i, len = PyList_GET_SIZE(self->added);
1556 Py_ssize_t i, len = PyList_GET_SIZE(self->added);
1558
1557
1559 for (i = start; i < len; i++) {
1558 for (i = start; i < len; i++) {
1560 PyObject *tuple = PyList_GET_ITEM(self->added, i);
1559 PyObject *tuple = PyList_GET_ITEM(self->added, i);
1561 PyObject *node = PyTuple_GET_ITEM(tuple, 7);
1560 PyObject *node = PyTuple_GET_ITEM(tuple, 7);
1562
1561
1563 nt_insert(self, PyString_AS_STRING(node), -1);
1562 nt_insert(self, PyString_AS_STRING(node), -1);
1564 }
1563 }
1565
1564
1566 if (start == 0)
1565 if (start == 0)
1567 Py_CLEAR(self->added);
1566 Py_CLEAR(self->added);
1568 }
1567 }
1569
1568
1570 /*
1569 /*
1571 * Delete a numeric range of revs, which must be at the end of the
1570 * Delete a numeric range of revs, which must be at the end of the
1572 * range, but exclude the sentinel nullid entry.
1571 * range, but exclude the sentinel nullid entry.
1573 */
1572 */
1574 static int index_slice_del(indexObject *self, PyObject *item)
1573 static int index_slice_del(indexObject *self, PyObject *item)
1575 {
1574 {
1576 Py_ssize_t start, stop, step, slicelength;
1575 Py_ssize_t start, stop, step, slicelength;
1577 Py_ssize_t length = index_length(self);
1576 Py_ssize_t length = index_length(self);
1578 int ret = 0;
1577 int ret = 0;
1579
1578
1580 if (PySlice_GetIndicesEx((PySliceObject*)item, length,
1579 if (PySlice_GetIndicesEx((PySliceObject*)item, length,
1581 &start, &stop, &step, &slicelength) < 0)
1580 &start, &stop, &step, &slicelength) < 0)
1582 return -1;
1581 return -1;
1583
1582
1584 if (slicelength <= 0)
1583 if (slicelength <= 0)
1585 return 0;
1584 return 0;
1586
1585
1587 if ((step < 0 && start < stop) || (step > 0 && start > stop))
1586 if ((step < 0 && start < stop) || (step > 0 && start > stop))
1588 stop = start;
1587 stop = start;
1589
1588
1590 if (step < 0) {
1589 if (step < 0) {
1591 stop = start + 1;
1590 stop = start + 1;
1592 start = stop + step*(slicelength - 1) - 1;
1591 start = stop + step*(slicelength - 1) - 1;
1593 step = -step;
1592 step = -step;
1594 }
1593 }
1595
1594
1596 if (step != 1) {
1595 if (step != 1) {
1597 PyErr_SetString(PyExc_ValueError,
1596 PyErr_SetString(PyExc_ValueError,
1598 "revlog index delete requires step size of 1");
1597 "revlog index delete requires step size of 1");
1599 return -1;
1598 return -1;
1600 }
1599 }
1601
1600
1602 if (stop != length - 1) {
1601 if (stop != length - 1) {
1603 PyErr_SetString(PyExc_IndexError,
1602 PyErr_SetString(PyExc_IndexError,
1604 "revlog index deletion indices are invalid");
1603 "revlog index deletion indices are invalid");
1605 return -1;
1604 return -1;
1606 }
1605 }
1607
1606
1608 if (start < self->length - 1) {
1607 if (start < self->length - 1) {
1609 if (self->nt) {
1608 if (self->nt) {
1610 Py_ssize_t i;
1609 Py_ssize_t i;
1611
1610
1612 for (i = start + 1; i < self->length - 1; i++) {
1611 for (i = start + 1; i < self->length - 1; i++) {
1613 const char *node = index_node(self, i);
1612 const char *node = index_node(self, i);
1614
1613
1615 if (node)
1614 if (node)
1616 nt_insert(self, node, -1);
1615 nt_insert(self, node, -1);
1617 }
1616 }
1618 if (self->added)
1617 if (self->added)
1619 nt_invalidate_added(self, 0);
1618 nt_invalidate_added(self, 0);
1620 if (self->ntrev > start)
1619 if (self->ntrev > start)
1621 self->ntrev = (int)start;
1620 self->ntrev = (int)start;
1622 }
1621 }
1623 self->length = start + 1;
1622 self->length = start + 1;
1624 if (start < self->raw_length) {
1623 if (start < self->raw_length) {
1625 if (self->cache) {
1624 if (self->cache) {
1626 Py_ssize_t i;
1625 Py_ssize_t i;
1627 for (i = start; i < self->raw_length; i++)
1626 for (i = start; i < self->raw_length; i++)
1628 Py_CLEAR(self->cache[i]);
1627 Py_CLEAR(self->cache[i]);
1629 }
1628 }
1630 self->raw_length = start;
1629 self->raw_length = start;
1631 }
1630 }
1632 goto done;
1631 goto done;
1633 }
1632 }
1634
1633
1635 if (self->nt) {
1634 if (self->nt) {
1636 nt_invalidate_added(self, start - self->length + 1);
1635 nt_invalidate_added(self, start - self->length + 1);
1637 if (self->ntrev > start)
1636 if (self->ntrev > start)
1638 self->ntrev = (int)start;
1637 self->ntrev = (int)start;
1639 }
1638 }
1640 if (self->added)
1639 if (self->added)
1641 ret = PyList_SetSlice(self->added, start - self->length + 1,
1640 ret = PyList_SetSlice(self->added, start - self->length + 1,
1642 PyList_GET_SIZE(self->added), NULL);
1641 PyList_GET_SIZE(self->added), NULL);
1643 done:
1642 done:
1644 Py_CLEAR(self->headrevs);
1643 Py_CLEAR(self->headrevs);
1645 return ret;
1644 return ret;
1646 }
1645 }
1647
1646
1648 /*
1647 /*
1649 * Supported ops:
1648 * Supported ops:
1650 *
1649 *
1651 * slice deletion
1650 * slice deletion
1652 * string assignment (extend node->rev mapping)
1651 * string assignment (extend node->rev mapping)
1653 * string deletion (shrink node->rev mapping)
1652 * string deletion (shrink node->rev mapping)
1654 */
1653 */
1655 static int index_assign_subscript(indexObject *self, PyObject *item,
1654 static int index_assign_subscript(indexObject *self, PyObject *item,
1656 PyObject *value)
1655 PyObject *value)
1657 {
1656 {
1658 char *node;
1657 char *node;
1659 Py_ssize_t nodelen;
1658 Py_ssize_t nodelen;
1660 long rev;
1659 long rev;
1661
1660
1662 if (PySlice_Check(item) && value == NULL)
1661 if (PySlice_Check(item) && value == NULL)
1663 return index_slice_del(self, item);
1662 return index_slice_del(self, item);
1664
1663
1665 if (node_check(item, &node, &nodelen) == -1)
1664 if (node_check(item, &node, &nodelen) == -1)
1666 return -1;
1665 return -1;
1667
1666
1668 if (value == NULL)
1667 if (value == NULL)
1669 return self->nt ? nt_insert(self, node, -1) : 0;
1668 return self->nt ? nt_insert(self, node, -1) : 0;
1670 rev = PyInt_AsLong(value);
1669 rev = PyInt_AsLong(value);
1671 if (rev > INT_MAX || rev < 0) {
1670 if (rev > INT_MAX || rev < 0) {
1672 if (!PyErr_Occurred())
1671 if (!PyErr_Occurred())
1673 PyErr_SetString(PyExc_ValueError, "rev out of range");
1672 PyErr_SetString(PyExc_ValueError, "rev out of range");
1674 return -1;
1673 return -1;
1675 }
1674 }
1676 return nt_insert(self, node, (int)rev);
1675 return nt_insert(self, node, (int)rev);
1677 }
1676 }
1678
1677
1679 /*
1678 /*
1680 * Find all RevlogNG entries in an index that has inline data. Update
1679 * Find all RevlogNG entries in an index that has inline data. Update
1681 * the optional "offsets" table with those entries.
1680 * the optional "offsets" table with those entries.
1682 */
1681 */
1683 static long inline_scan(indexObject *self, const char **offsets)
1682 static long inline_scan(indexObject *self, const char **offsets)
1684 {
1683 {
1685 const char *data = PyString_AS_STRING(self->data);
1684 const char *data = PyString_AS_STRING(self->data);
1686 const char *end = data + PyString_GET_SIZE(self->data);
1685 Py_ssize_t pos = 0;
1686 Py_ssize_t end = PyString_GET_SIZE(self->data);
1687 long incr = v1_hdrsize;
1687 long incr = v1_hdrsize;
1688 Py_ssize_t len = 0;
1688 Py_ssize_t len = 0;
1689
1689
1690 while (data + v1_hdrsize <= end) {
1690 while (pos + v1_hdrsize <= end && pos >= 0) {
1691 uint32_t comp_len;
1691 uint32_t comp_len;
1692 const char *old_data;
1693 /* 3rd element of header is length of compressed inline data */
1692 /* 3rd element of header is length of compressed inline data */
1694 comp_len = getbe32(data + 8);
1693 comp_len = getbe32(data + pos + 8);
1695 incr = v1_hdrsize + comp_len;
1694 incr = v1_hdrsize + comp_len;
1696 if (incr < v1_hdrsize)
1697 break;
1698 if (offsets)
1695 if (offsets)
1699 offsets[len] = data;
1696 offsets[len] = data + pos;
1700 len++;
1697 len++;
1701 old_data = data;
1698 pos += incr;
1702 data += incr;
1703 if (data <= old_data)
1704 break;
1705 }
1699 }
1706
1700
1707 if (data != end && data + v1_hdrsize != end) {
1701 if (pos != end) {
1708 if (!PyErr_Occurred())
1702 if (!PyErr_Occurred())
1709 PyErr_SetString(PyExc_ValueError, "corrupt index file");
1703 PyErr_SetString(PyExc_ValueError, "corrupt index file");
1710 return -1;
1704 return -1;
1711 }
1705 }
1712
1706
1713 return len;
1707 return len;
1714 }
1708 }
1715
1709
1716 static int index_init(indexObject *self, PyObject *args)
1710 static int index_init(indexObject *self, PyObject *args)
1717 {
1711 {
1718 PyObject *data_obj, *inlined_obj;
1712 PyObject *data_obj, *inlined_obj;
1719 Py_ssize_t size;
1713 Py_ssize_t size;
1720
1714
1721 /* Initialize before argument-checking to avoid index_dealloc() crash. */
1715 /* Initialize before argument-checking to avoid index_dealloc() crash. */
1722 self->raw_length = 0;
1716 self->raw_length = 0;
1723 self->added = NULL;
1717 self->added = NULL;
1724 self->cache = NULL;
1718 self->cache = NULL;
1725 self->data = NULL;
1719 self->data = NULL;
1726 self->headrevs = NULL;
1720 self->headrevs = NULL;
1727 self->nt = NULL;
1721 self->nt = NULL;
1728 self->offsets = NULL;
1722 self->offsets = NULL;
1729
1723
1730 if (!PyArg_ParseTuple(args, "OO", &data_obj, &inlined_obj))
1724 if (!PyArg_ParseTuple(args, "OO", &data_obj, &inlined_obj))
1731 return -1;
1725 return -1;
1732 if (!PyString_Check(data_obj)) {
1726 if (!PyString_Check(data_obj)) {
1733 PyErr_SetString(PyExc_TypeError, "data is not a string");
1727 PyErr_SetString(PyExc_TypeError, "data is not a string");
1734 return -1;
1728 return -1;
1735 }
1729 }
1736 size = PyString_GET_SIZE(data_obj);
1730 size = PyString_GET_SIZE(data_obj);
1737
1731
1738 self->inlined = inlined_obj && PyObject_IsTrue(inlined_obj);
1732 self->inlined = inlined_obj && PyObject_IsTrue(inlined_obj);
1739 self->data = data_obj;
1733 self->data = data_obj;
1740
1734
1741 self->ntlength = self->ntcapacity = 0;
1735 self->ntlength = self->ntcapacity = 0;
1742 self->ntdepth = self->ntsplits = 0;
1736 self->ntdepth = self->ntsplits = 0;
1743 self->ntlookups = self->ntmisses = 0;
1737 self->ntlookups = self->ntmisses = 0;
1744 self->ntrev = -1;
1738 self->ntrev = -1;
1745 Py_INCREF(self->data);
1739 Py_INCREF(self->data);
1746
1740
1747 if (self->inlined) {
1741 if (self->inlined) {
1748 long len = inline_scan(self, NULL);
1742 long len = inline_scan(self, NULL);
1749 if (len == -1)
1743 if (len == -1)
1750 goto bail;
1744 goto bail;
1751 self->raw_length = len;
1745 self->raw_length = len;
1752 self->length = len + 1;
1746 self->length = len + 1;
1753 } else {
1747 } else {
1754 if (size % v1_hdrsize) {
1748 if (size % v1_hdrsize) {
1755 PyErr_SetString(PyExc_ValueError, "corrupt index file");
1749 PyErr_SetString(PyExc_ValueError, "corrupt index file");
1756 goto bail;
1750 goto bail;
1757 }
1751 }
1758 self->raw_length = size / v1_hdrsize;
1752 self->raw_length = size / v1_hdrsize;
1759 self->length = self->raw_length + 1;
1753 self->length = self->raw_length + 1;
1760 }
1754 }
1761
1755
1762 return 0;
1756 return 0;
1763 bail:
1757 bail:
1764 return -1;
1758 return -1;
1765 }
1759 }
1766
1760
1767 static PyObject *index_nodemap(indexObject *self)
1761 static PyObject *index_nodemap(indexObject *self)
1768 {
1762 {
1769 Py_INCREF(self);
1763 Py_INCREF(self);
1770 return (PyObject *)self;
1764 return (PyObject *)self;
1771 }
1765 }
1772
1766
1773 static void index_dealloc(indexObject *self)
1767 static void index_dealloc(indexObject *self)
1774 {
1768 {
1775 _index_clearcaches(self);
1769 _index_clearcaches(self);
1776 Py_XDECREF(self->data);
1770 Py_XDECREF(self->data);
1777 Py_XDECREF(self->added);
1771 Py_XDECREF(self->added);
1778 PyObject_Del(self);
1772 PyObject_Del(self);
1779 }
1773 }
1780
1774
1781 static PySequenceMethods index_sequence_methods = {
1775 static PySequenceMethods index_sequence_methods = {
1782 (lenfunc)index_length, /* sq_length */
1776 (lenfunc)index_length, /* sq_length */
1783 0, /* sq_concat */
1777 0, /* sq_concat */
1784 0, /* sq_repeat */
1778 0, /* sq_repeat */
1785 (ssizeargfunc)index_get, /* sq_item */
1779 (ssizeargfunc)index_get, /* sq_item */
1786 0, /* sq_slice */
1780 0, /* sq_slice */
1787 0, /* sq_ass_item */
1781 0, /* sq_ass_item */
1788 0, /* sq_ass_slice */
1782 0, /* sq_ass_slice */
1789 (objobjproc)index_contains, /* sq_contains */
1783 (objobjproc)index_contains, /* sq_contains */
1790 };
1784 };
1791
1785
1792 static PyMappingMethods index_mapping_methods = {
1786 static PyMappingMethods index_mapping_methods = {
1793 (lenfunc)index_length, /* mp_length */
1787 (lenfunc)index_length, /* mp_length */
1794 (binaryfunc)index_getitem, /* mp_subscript */
1788 (binaryfunc)index_getitem, /* mp_subscript */
1795 (objobjargproc)index_assign_subscript, /* mp_ass_subscript */
1789 (objobjargproc)index_assign_subscript, /* mp_ass_subscript */
1796 };
1790 };
1797
1791
1798 static PyMethodDef index_methods[] = {
1792 static PyMethodDef index_methods[] = {
1799 {"ancestors", (PyCFunction)index_ancestors, METH_VARARGS,
1793 {"ancestors", (PyCFunction)index_ancestors, METH_VARARGS,
1800 "return the gca set of the given revs"},
1794 "return the gca set of the given revs"},
1801 {"clearcaches", (PyCFunction)index_clearcaches, METH_NOARGS,
1795 {"clearcaches", (PyCFunction)index_clearcaches, METH_NOARGS,
1802 "clear the index caches"},
1796 "clear the index caches"},
1803 {"get", (PyCFunction)index_m_get, METH_VARARGS,
1797 {"get", (PyCFunction)index_m_get, METH_VARARGS,
1804 "get an index entry"},
1798 "get an index entry"},
1805 {"headrevs", (PyCFunction)index_headrevs, METH_NOARGS,
1799 {"headrevs", (PyCFunction)index_headrevs, METH_NOARGS,
1806 "get head revisions"},
1800 "get head revisions"},
1807 {"insert", (PyCFunction)index_insert, METH_VARARGS,
1801 {"insert", (PyCFunction)index_insert, METH_VARARGS,
1808 "insert an index entry"},
1802 "insert an index entry"},
1809 {"partialmatch", (PyCFunction)index_partialmatch, METH_VARARGS,
1803 {"partialmatch", (PyCFunction)index_partialmatch, METH_VARARGS,
1810 "match a potentially ambiguous node ID"},
1804 "match a potentially ambiguous node ID"},
1811 {"stats", (PyCFunction)index_stats, METH_NOARGS,
1805 {"stats", (PyCFunction)index_stats, METH_NOARGS,
1812 "stats for the index"},
1806 "stats for the index"},
1813 {NULL} /* Sentinel */
1807 {NULL} /* Sentinel */
1814 };
1808 };
1815
1809
1816 static PyGetSetDef index_getset[] = {
1810 static PyGetSetDef index_getset[] = {
1817 {"nodemap", (getter)index_nodemap, NULL, "nodemap", NULL},
1811 {"nodemap", (getter)index_nodemap, NULL, "nodemap", NULL},
1818 {NULL} /* Sentinel */
1812 {NULL} /* Sentinel */
1819 };
1813 };
1820
1814
1821 static PyTypeObject indexType = {
1815 static PyTypeObject indexType = {
1822 PyObject_HEAD_INIT(NULL)
1816 PyObject_HEAD_INIT(NULL)
1823 0, /* ob_size */
1817 0, /* ob_size */
1824 "parsers.index", /* tp_name */
1818 "parsers.index", /* tp_name */
1825 sizeof(indexObject), /* tp_basicsize */
1819 sizeof(indexObject), /* tp_basicsize */
1826 0, /* tp_itemsize */
1820 0, /* tp_itemsize */
1827 (destructor)index_dealloc, /* tp_dealloc */
1821 (destructor)index_dealloc, /* tp_dealloc */
1828 0, /* tp_print */
1822 0, /* tp_print */
1829 0, /* tp_getattr */
1823 0, /* tp_getattr */
1830 0, /* tp_setattr */
1824 0, /* tp_setattr */
1831 0, /* tp_compare */
1825 0, /* tp_compare */
1832 0, /* tp_repr */
1826 0, /* tp_repr */
1833 0, /* tp_as_number */
1827 0, /* tp_as_number */
1834 &index_sequence_methods, /* tp_as_sequence */
1828 &index_sequence_methods, /* tp_as_sequence */
1835 &index_mapping_methods, /* tp_as_mapping */
1829 &index_mapping_methods, /* tp_as_mapping */
1836 0, /* tp_hash */
1830 0, /* tp_hash */
1837 0, /* tp_call */
1831 0, /* tp_call */
1838 0, /* tp_str */
1832 0, /* tp_str */
1839 0, /* tp_getattro */
1833 0, /* tp_getattro */
1840 0, /* tp_setattro */
1834 0, /* tp_setattro */
1841 0, /* tp_as_buffer */
1835 0, /* tp_as_buffer */
1842 Py_TPFLAGS_DEFAULT, /* tp_flags */
1836 Py_TPFLAGS_DEFAULT, /* tp_flags */
1843 "revlog index", /* tp_doc */
1837 "revlog index", /* tp_doc */
1844 0, /* tp_traverse */
1838 0, /* tp_traverse */
1845 0, /* tp_clear */
1839 0, /* tp_clear */
1846 0, /* tp_richcompare */
1840 0, /* tp_richcompare */
1847 0, /* tp_weaklistoffset */
1841 0, /* tp_weaklistoffset */
1848 0, /* tp_iter */
1842 0, /* tp_iter */
1849 0, /* tp_iternext */
1843 0, /* tp_iternext */
1850 index_methods, /* tp_methods */
1844 index_methods, /* tp_methods */
1851 0, /* tp_members */
1845 0, /* tp_members */
1852 index_getset, /* tp_getset */
1846 index_getset, /* tp_getset */
1853 0, /* tp_base */
1847 0, /* tp_base */
1854 0, /* tp_dict */
1848 0, /* tp_dict */
1855 0, /* tp_descr_get */
1849 0, /* tp_descr_get */
1856 0, /* tp_descr_set */
1850 0, /* tp_descr_set */
1857 0, /* tp_dictoffset */
1851 0, /* tp_dictoffset */
1858 (initproc)index_init, /* tp_init */
1852 (initproc)index_init, /* tp_init */
1859 0, /* tp_alloc */
1853 0, /* tp_alloc */
1860 };
1854 };
1861
1855
1862 /*
1856 /*
1863 * returns a tuple of the form (index, index, cache) with elements as
1857 * returns a tuple of the form (index, index, cache) with elements as
1864 * follows:
1858 * follows:
1865 *
1859 *
1866 * index: an index object that lazily parses RevlogNG records
1860 * index: an index object that lazily parses RevlogNG records
1867 * cache: if data is inlined, a tuple (index_file_content, 0), else None
1861 * cache: if data is inlined, a tuple (index_file_content, 0), else None
1868 *
1862 *
1869 * added complications are for backwards compatibility
1863 * added complications are for backwards compatibility
1870 */
1864 */
1871 static PyObject *parse_index2(PyObject *self, PyObject *args)
1865 static PyObject *parse_index2(PyObject *self, PyObject *args)
1872 {
1866 {
1873 PyObject *tuple = NULL, *cache = NULL;
1867 PyObject *tuple = NULL, *cache = NULL;
1874 indexObject *idx;
1868 indexObject *idx;
1875 int ret;
1869 int ret;
1876
1870
1877 idx = PyObject_New(indexObject, &indexType);
1871 idx = PyObject_New(indexObject, &indexType);
1878 if (idx == NULL)
1872 if (idx == NULL)
1879 goto bail;
1873 goto bail;
1880
1874
1881 ret = index_init(idx, args);
1875 ret = index_init(idx, args);
1882 if (ret == -1)
1876 if (ret == -1)
1883 goto bail;
1877 goto bail;
1884
1878
1885 if (idx->inlined) {
1879 if (idx->inlined) {
1886 cache = Py_BuildValue("iO", 0, idx->data);
1880 cache = Py_BuildValue("iO", 0, idx->data);
1887 if (cache == NULL)
1881 if (cache == NULL)
1888 goto bail;
1882 goto bail;
1889 } else {
1883 } else {
1890 cache = Py_None;
1884 cache = Py_None;
1891 Py_INCREF(cache);
1885 Py_INCREF(cache);
1892 }
1886 }
1893
1887
1894 tuple = Py_BuildValue("NN", idx, cache);
1888 tuple = Py_BuildValue("NN", idx, cache);
1895 if (!tuple)
1889 if (!tuple)
1896 goto bail;
1890 goto bail;
1897 return tuple;
1891 return tuple;
1898
1892
1899 bail:
1893 bail:
1900 Py_XDECREF(idx);
1894 Py_XDECREF(idx);
1901 Py_XDECREF(cache);
1895 Py_XDECREF(cache);
1902 Py_XDECREF(tuple);
1896 Py_XDECREF(tuple);
1903 return NULL;
1897 return NULL;
1904 }
1898 }
1905
1899
1906 static char parsers_doc[] = "Efficient content parsing.";
1900 static char parsers_doc[] = "Efficient content parsing.";
1907
1901
1908 PyObject *encodedir(PyObject *self, PyObject *args);
1902 PyObject *encodedir(PyObject *self, PyObject *args);
1909 PyObject *pathencode(PyObject *self, PyObject *args);
1903 PyObject *pathencode(PyObject *self, PyObject *args);
1910 PyObject *lowerencode(PyObject *self, PyObject *args);
1904 PyObject *lowerencode(PyObject *self, PyObject *args);
1911
1905
1912 static PyMethodDef methods[] = {
1906 static PyMethodDef methods[] = {
1913 {"pack_dirstate", pack_dirstate, METH_VARARGS, "pack a dirstate\n"},
1907 {"pack_dirstate", pack_dirstate, METH_VARARGS, "pack a dirstate\n"},
1914 {"parse_manifest", parse_manifest, METH_VARARGS, "parse a manifest\n"},
1908 {"parse_manifest", parse_manifest, METH_VARARGS, "parse a manifest\n"},
1915 {"parse_dirstate", parse_dirstate, METH_VARARGS, "parse a dirstate\n"},
1909 {"parse_dirstate", parse_dirstate, METH_VARARGS, "parse a dirstate\n"},
1916 {"parse_index2", parse_index2, METH_VARARGS, "parse a revlog index\n"},
1910 {"parse_index2", parse_index2, METH_VARARGS, "parse a revlog index\n"},
1917 {"encodedir", encodedir, METH_VARARGS, "encodedir a path\n"},
1911 {"encodedir", encodedir, METH_VARARGS, "encodedir a path\n"},
1918 {"pathencode", pathencode, METH_VARARGS, "fncache-encode a path\n"},
1912 {"pathencode", pathencode, METH_VARARGS, "fncache-encode a path\n"},
1919 {"lowerencode", lowerencode, METH_VARARGS, "lower-encode a path\n"},
1913 {"lowerencode", lowerencode, METH_VARARGS, "lower-encode a path\n"},
1920 {NULL, NULL}
1914 {NULL, NULL}
1921 };
1915 };
1922
1916
1923 void dirs_module_init(PyObject *mod);
1917 void dirs_module_init(PyObject *mod);
1924
1918
1925 static void module_init(PyObject *mod)
1919 static void module_init(PyObject *mod)
1926 {
1920 {
1927 dirs_module_init(mod);
1921 dirs_module_init(mod);
1928
1922
1929 indexType.tp_new = PyType_GenericNew;
1923 indexType.tp_new = PyType_GenericNew;
1930 if (PyType_Ready(&indexType) < 0)
1924 if (PyType_Ready(&indexType) < 0)
1931 return;
1925 return;
1932 Py_INCREF(&indexType);
1926 Py_INCREF(&indexType);
1933
1927
1934 PyModule_AddObject(mod, "index", (PyObject *)&indexType);
1928 PyModule_AddObject(mod, "index", (PyObject *)&indexType);
1935
1929
1936 nullentry = Py_BuildValue("iiiiiiis#", 0, 0, 0,
1930 nullentry = Py_BuildValue("iiiiiiis#", 0, 0, 0,
1937 -1, -1, -1, -1, nullid, 20);
1931 -1, -1, -1, -1, nullid, 20);
1938 if (nullentry)
1932 if (nullentry)
1939 PyObject_GC_UnTrack(nullentry);
1933 PyObject_GC_UnTrack(nullentry);
1940
1934
1941 dirstate_unset = Py_BuildValue("ciii", 'n', 0, -1, -1);
1935 dirstate_unset = Py_BuildValue("ciii", 'n', 0, -1, -1);
1942 }
1936 }
1943
1937
1944 #ifdef IS_PY3K
1938 #ifdef IS_PY3K
1945 static struct PyModuleDef parsers_module = {
1939 static struct PyModuleDef parsers_module = {
1946 PyModuleDef_HEAD_INIT,
1940 PyModuleDef_HEAD_INIT,
1947 "parsers",
1941 "parsers",
1948 parsers_doc,
1942 parsers_doc,
1949 -1,
1943 -1,
1950 methods
1944 methods
1951 };
1945 };
1952
1946
1953 PyMODINIT_FUNC PyInit_parsers(void)
1947 PyMODINIT_FUNC PyInit_parsers(void)
1954 {
1948 {
1955 PyObject *mod = PyModule_Create(&parsers_module);
1949 PyObject *mod = PyModule_Create(&parsers_module);
1956 module_init(mod);
1950 module_init(mod);
1957 return mod;
1951 return mod;
1958 }
1952 }
1959 #else
1953 #else
1960 PyMODINIT_FUNC initparsers(void)
1954 PyMODINIT_FUNC initparsers(void)
1961 {
1955 {
1962 PyObject *mod = Py_InitModule3("parsers", methods, parsers_doc);
1956 PyObject *mod = Py_InitModule3("parsers", methods, parsers_doc);
1963 module_init(mod);
1957 module_init(mod);
1964 }
1958 }
1965 #endif
1959 #endif
General Comments 0
You need to be logged in to leave comments. Login now