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