##// END OF EJS Templates
parsers: replace magic number 64 with symbolic constant
Bryan O'Sullivan -
r16863:bbedef66 default
parent child Browse files
Show More
@@ -1,1399 +1,1401 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 <string.h>
13 13
14 14 #include "util.h"
15 15
16 16 static inline int hexdigit(const char *p, Py_ssize_t off)
17 17 {
18 18 char c = p[off];
19 19
20 20 if (c >= '0' && c <= '9')
21 21 return c - '0';
22 22 if (c >= 'a' && c <= 'f')
23 23 return c - 'a' + 10;
24 24 if (c >= 'A' && c <= 'F')
25 25 return c - 'A' + 10;
26 26
27 27 PyErr_SetString(PyExc_ValueError, "input contains non-hex character");
28 28 return 0;
29 29 }
30 30
31 31 /*
32 32 * Turn a hex-encoded string into binary.
33 33 */
34 34 static PyObject *unhexlify(const char *str, int len)
35 35 {
36 36 PyObject *ret;
37 37 char *d;
38 38 int i;
39 39
40 40 ret = PyBytes_FromStringAndSize(NULL, len / 2);
41 41
42 42 if (!ret)
43 43 return NULL;
44 44
45 45 d = PyBytes_AsString(ret);
46 46
47 47 for (i = 0; i < len;) {
48 48 int hi = hexdigit(str, i++);
49 49 int lo = hexdigit(str, i++);
50 50 *d++ = (hi << 4) | lo;
51 51 }
52 52
53 53 return ret;
54 54 }
55 55
56 56 /*
57 57 * This code assumes that a manifest is stitched together with newline
58 58 * ('\n') characters.
59 59 */
60 60 static PyObject *parse_manifest(PyObject *self, PyObject *args)
61 61 {
62 62 PyObject *mfdict, *fdict;
63 63 char *str, *cur, *start, *zero;
64 64 int len;
65 65
66 66 if (!PyArg_ParseTuple(args, "O!O!s#:parse_manifest",
67 67 &PyDict_Type, &mfdict,
68 68 &PyDict_Type, &fdict,
69 69 &str, &len))
70 70 goto quit;
71 71
72 72 for (start = cur = str, zero = NULL; cur < str + len; cur++) {
73 73 PyObject *file = NULL, *node = NULL;
74 74 PyObject *flags = NULL;
75 75 int nlen;
76 76
77 77 if (!*cur) {
78 78 zero = cur;
79 79 continue;
80 80 }
81 81 else if (*cur != '\n')
82 82 continue;
83 83
84 84 if (!zero) {
85 85 PyErr_SetString(PyExc_ValueError,
86 86 "manifest entry has no separator");
87 87 goto quit;
88 88 }
89 89
90 90 file = PyBytes_FromStringAndSize(start, zero - start);
91 91
92 92 if (!file)
93 93 goto bail;
94 94
95 95 nlen = cur - zero - 1;
96 96
97 97 node = unhexlify(zero + 1, nlen > 40 ? 40 : nlen);
98 98 if (!node)
99 99 goto bail;
100 100
101 101 if (nlen > 40) {
102 102 flags = PyBytes_FromStringAndSize(zero + 41,
103 103 nlen - 40);
104 104 if (!flags)
105 105 goto bail;
106 106
107 107 if (PyDict_SetItem(fdict, file, flags) == -1)
108 108 goto bail;
109 109 }
110 110
111 111 if (PyDict_SetItem(mfdict, file, node) == -1)
112 112 goto bail;
113 113
114 114 start = cur + 1;
115 115 zero = NULL;
116 116
117 117 Py_XDECREF(flags);
118 118 Py_XDECREF(node);
119 119 Py_XDECREF(file);
120 120 continue;
121 121 bail:
122 122 Py_XDECREF(flags);
123 123 Py_XDECREF(node);
124 124 Py_XDECREF(file);
125 125 goto quit;
126 126 }
127 127
128 128 if (len > 0 && *(cur - 1) != '\n') {
129 129 PyErr_SetString(PyExc_ValueError,
130 130 "manifest contains trailing garbage");
131 131 goto quit;
132 132 }
133 133
134 134 Py_INCREF(Py_None);
135 135 return Py_None;
136 136 quit:
137 137 return NULL;
138 138 }
139 139
140 140 static PyObject *parse_dirstate(PyObject *self, PyObject *args)
141 141 {
142 142 PyObject *dmap, *cmap, *parents = NULL, *ret = NULL;
143 143 PyObject *fname = NULL, *cname = NULL, *entry = NULL;
144 144 char *str, *cur, *end, *cpos;
145 145 int state, mode, size, mtime;
146 146 unsigned int flen;
147 147 int len;
148 148
149 149 if (!PyArg_ParseTuple(args, "O!O!s#:parse_dirstate",
150 150 &PyDict_Type, &dmap,
151 151 &PyDict_Type, &cmap,
152 152 &str, &len))
153 153 goto quit;
154 154
155 155 /* read parents */
156 156 if (len < 40)
157 157 goto quit;
158 158
159 159 parents = Py_BuildValue("s#s#", str, 20, str + 20, 20);
160 160 if (!parents)
161 161 goto quit;
162 162
163 163 /* read filenames */
164 164 cur = str + 40;
165 165 end = str + len;
166 166
167 167 while (cur < end - 17) {
168 168 /* unpack header */
169 169 state = *cur;
170 170 mode = getbe32(cur + 1);
171 171 size = getbe32(cur + 5);
172 172 mtime = getbe32(cur + 9);
173 173 flen = getbe32(cur + 13);
174 174 cur += 17;
175 175 if (cur + flen > end || cur + flen < cur) {
176 176 PyErr_SetString(PyExc_ValueError, "overflow in dirstate");
177 177 goto quit;
178 178 }
179 179
180 180 entry = Py_BuildValue("ciii", state, mode, size, mtime);
181 181 if (!entry)
182 182 goto quit;
183 183 PyObject_GC_UnTrack(entry); /* don't waste time with this */
184 184
185 185 cpos = memchr(cur, 0, flen);
186 186 if (cpos) {
187 187 fname = PyBytes_FromStringAndSize(cur, cpos - cur);
188 188 cname = PyBytes_FromStringAndSize(cpos + 1,
189 189 flen - (cpos - cur) - 1);
190 190 if (!fname || !cname ||
191 191 PyDict_SetItem(cmap, fname, cname) == -1 ||
192 192 PyDict_SetItem(dmap, fname, entry) == -1)
193 193 goto quit;
194 194 Py_DECREF(cname);
195 195 } else {
196 196 fname = PyBytes_FromStringAndSize(cur, flen);
197 197 if (!fname ||
198 198 PyDict_SetItem(dmap, fname, entry) == -1)
199 199 goto quit;
200 200 }
201 201 cur += flen;
202 202 Py_DECREF(fname);
203 203 Py_DECREF(entry);
204 204 fname = cname = entry = NULL;
205 205 }
206 206
207 207 ret = parents;
208 208 Py_INCREF(ret);
209 209 quit:
210 210 Py_XDECREF(fname);
211 211 Py_XDECREF(cname);
212 212 Py_XDECREF(entry);
213 213 Py_XDECREF(parents);
214 214 return ret;
215 215 }
216 216
217 217 /*
218 218 * A base-16 trie for fast node->rev mapping.
219 219 *
220 220 * Positive value is index of the next node in the trie
221 221 * Negative value is a leaf: -(rev + 1)
222 222 * Zero is empty
223 223 */
224 224 typedef struct {
225 225 int children[16];
226 226 } nodetree;
227 227
228 228 /*
229 229 * This class has two behaviours.
230 230 *
231 231 * When used in a list-like way (with integer keys), we decode an
232 232 * entry in a RevlogNG index file on demand. Our last entry is a
233 233 * sentinel, always a nullid. We have limited support for
234 234 * integer-keyed insert and delete, only at elements right before the
235 235 * sentinel.
236 236 *
237 237 * With string keys, we lazily perform a reverse mapping from node to
238 238 * rev, using a base-16 trie.
239 239 */
240 240 typedef struct {
241 241 PyObject_HEAD
242 242 /* Type-specific fields go here. */
243 243 PyObject *data; /* raw bytes of index */
244 244 PyObject **cache; /* cached tuples */
245 245 const char **offsets; /* populated on demand */
246 246 Py_ssize_t raw_length; /* original number of elements */
247 247 Py_ssize_t length; /* current number of elements */
248 248 PyObject *added; /* populated on demand */
249 249 PyObject *headrevs; /* cache, invalidated on changes */
250 250 nodetree *nt; /* base-16 trie */
251 251 int ntlength; /* # nodes in use */
252 252 int ntcapacity; /* # nodes allocated */
253 253 int ntdepth; /* maximum depth of tree */
254 254 int ntsplits; /* # splits performed */
255 255 int ntrev; /* last rev scanned */
256 256 int ntlookups; /* # lookups */
257 257 int ntmisses; /* # lookups that miss the cache */
258 258 int inlined;
259 259 } indexObject;
260 260
261 261 static Py_ssize_t index_length(const indexObject *self)
262 262 {
263 263 if (self->added == NULL)
264 264 return self->length;
265 265 return self->length + PyList_GET_SIZE(self->added);
266 266 }
267 267
268 268 static PyObject *nullentry;
269 269 static const char nullid[20];
270 270
271 271 static long inline_scan(indexObject *self, const char **offsets);
272 272
273 273 #if LONG_MAX == 0x7fffffffL
274 274 static char *tuple_format = "Kiiiiiis#";
275 275 #else
276 276 static char *tuple_format = "kiiiiiis#";
277 277 #endif
278 278
279 /* A RevlogNG v1 index entry is 64 bytes long. */
280 static const long v1_hdrsize = 64;
281
279 282 /*
280 283 * Return a pointer to the beginning of a RevlogNG record.
281 284 */
282 285 static const char *index_deref(indexObject *self, Py_ssize_t pos)
283 286 {
284 287 if (self->inlined && pos > 0) {
285 288 if (self->offsets == NULL) {
286 289 self->offsets = malloc(self->raw_length *
287 290 sizeof(*self->offsets));
288 291 if (self->offsets == NULL)
289 292 return (const char *)PyErr_NoMemory();
290 293 inline_scan(self, self->offsets);
291 294 }
292 295 return self->offsets[pos];
293 296 }
294 297
295 return PyString_AS_STRING(self->data) + pos * 64;
298 return PyString_AS_STRING(self->data) + pos * v1_hdrsize;
296 299 }
297 300
298 301 /*
299 302 * RevlogNG format (all in big endian, data may be inlined):
300 303 * 6 bytes: offset
301 304 * 2 bytes: flags
302 305 * 4 bytes: compressed length
303 306 * 4 bytes: uncompressed length
304 307 * 4 bytes: base revision
305 308 * 4 bytes: link revision
306 309 * 4 bytes: parent 1 revision
307 310 * 4 bytes: parent 2 revision
308 311 * 32 bytes: nodeid (only 20 bytes used)
309 312 */
310 313 static PyObject *index_get(indexObject *self, Py_ssize_t pos)
311 314 {
312 315 uint64_t offset_flags;
313 316 int comp_len, uncomp_len, base_rev, link_rev, parent_1, parent_2;
314 317 const char *c_node_id;
315 318 const char *data;
316 319 Py_ssize_t length = index_length(self);
317 320 PyObject *entry;
318 321
319 322 if (pos < 0)
320 323 pos += length;
321 324
322 325 if (pos < 0 || pos >= length) {
323 326 PyErr_SetString(PyExc_IndexError, "revlog index out of range");
324 327 return NULL;
325 328 }
326 329
327 330 if (pos == length - 1) {
328 331 Py_INCREF(nullentry);
329 332 return nullentry;
330 333 }
331 334
332 335 if (pos >= self->length - 1) {
333 336 PyObject *obj;
334 337 obj = PyList_GET_ITEM(self->added, pos - self->length + 1);
335 338 Py_INCREF(obj);
336 339 return obj;
337 340 }
338 341
339 342 if (self->cache) {
340 343 if (self->cache[pos]) {
341 344 Py_INCREF(self->cache[pos]);
342 345 return self->cache[pos];
343 346 }
344 347 } else {
345 348 self->cache = calloc(self->raw_length, sizeof(PyObject *));
346 349 if (self->cache == NULL)
347 350 return PyErr_NoMemory();
348 351 }
349 352
350 353 data = index_deref(self, pos);
351 354 if (data == NULL)
352 355 return NULL;
353 356
354 357 offset_flags = getbe32(data + 4);
355 358 if (pos == 0) /* mask out version number for the first entry */
356 359 offset_flags &= 0xFFFF;
357 360 else {
358 361 uint32_t offset_high = getbe32(data);
359 362 offset_flags |= ((uint64_t)offset_high) << 32;
360 363 }
361 364
362 365 comp_len = getbe32(data + 8);
363 366 uncomp_len = getbe32(data + 12);
364 367 base_rev = getbe32(data + 16);
365 368 link_rev = getbe32(data + 20);
366 369 parent_1 = getbe32(data + 24);
367 370 parent_2 = getbe32(data + 28);
368 371 c_node_id = data + 32;
369 372
370 373 entry = Py_BuildValue(tuple_format, offset_flags, comp_len,
371 374 uncomp_len, base_rev, link_rev,
372 375 parent_1, parent_2, c_node_id, 20);
373 376
374 377 if (entry)
375 378 PyObject_GC_UnTrack(entry);
376 379
377 380 self->cache[pos] = entry;
378 381 Py_INCREF(entry);
379 382
380 383 return entry;
381 384 }
382 385
383 386 /*
384 387 * Return the 20-byte SHA of the node corresponding to the given rev.
385 388 */
386 389 static const char *index_node(indexObject *self, Py_ssize_t pos)
387 390 {
388 391 Py_ssize_t length = index_length(self);
389 392 const char *data;
390 393
391 394 if (pos == length - 1 || pos == INT_MAX)
392 395 return nullid;
393 396
394 397 if (pos >= length)
395 398 return NULL;
396 399
397 400 if (pos >= self->length - 1) {
398 401 PyObject *tuple, *str;
399 402 tuple = PyList_GET_ITEM(self->added, pos - self->length + 1);
400 403 str = PyTuple_GetItem(tuple, 7);
401 404 return str ? PyString_AS_STRING(str) : NULL;
402 405 }
403 406
404 407 data = index_deref(self, pos);
405 408 return data ? data + 32 : NULL;
406 409 }
407 410
408 411 static int nt_insert(indexObject *self, const char *node, int rev);
409 412
410 413 static int node_check(PyObject *obj, char **node, Py_ssize_t *nodelen)
411 414 {
412 415 if (PyString_AsStringAndSize(obj, node, nodelen) == -1)
413 416 return -1;
414 417 if (*nodelen == 20)
415 418 return 0;
416 419 PyErr_SetString(PyExc_ValueError, "20-byte hash required");
417 420 return -1;
418 421 }
419 422
420 423 static PyObject *index_insert(indexObject *self, PyObject *args)
421 424 {
422 425 PyObject *obj;
423 426 char *node;
424 427 long offset;
425 428 Py_ssize_t len, nodelen;
426 429
427 430 if (!PyArg_ParseTuple(args, "lO", &offset, &obj))
428 431 return NULL;
429 432
430 433 if (!PyTuple_Check(obj) || PyTuple_GET_SIZE(obj) != 8) {
431 434 PyErr_SetString(PyExc_TypeError, "8-tuple required");
432 435 return NULL;
433 436 }
434 437
435 438 if (node_check(PyTuple_GET_ITEM(obj, 7), &node, &nodelen) == -1)
436 439 return NULL;
437 440
438 441 len = index_length(self);
439 442
440 443 if (offset < 0)
441 444 offset += len;
442 445
443 446 if (offset != len - 1) {
444 447 PyErr_SetString(PyExc_IndexError,
445 448 "insert only supported at index -1");
446 449 return NULL;
447 450 }
448 451
449 452 if (offset > INT_MAX) {
450 453 PyErr_SetString(PyExc_ValueError,
451 454 "currently only 2**31 revs supported");
452 455 return NULL;
453 456 }
454 457
455 458 if (self->added == NULL) {
456 459 self->added = PyList_New(0);
457 460 if (self->added == NULL)
458 461 return NULL;
459 462 }
460 463
461 464 if (PyList_Append(self->added, obj) == -1)
462 465 return NULL;
463 466
464 467 if (self->nt)
465 468 nt_insert(self, node, (int)offset);
466 469
467 470 Py_CLEAR(self->headrevs);
468 471 Py_RETURN_NONE;
469 472 }
470 473
471 474 static void _index_clearcaches(indexObject *self)
472 475 {
473 476 if (self->cache) {
474 477 Py_ssize_t i;
475 478
476 479 for (i = 0; i < self->raw_length; i++)
477 480 Py_CLEAR(self->cache[i]);
478 481 free(self->cache);
479 482 self->cache = NULL;
480 483 }
481 484 if (self->offsets) {
482 485 free(self->offsets);
483 486 self->offsets = NULL;
484 487 }
485 488 if (self->nt) {
486 489 free(self->nt);
487 490 self->nt = NULL;
488 491 }
489 492 Py_CLEAR(self->headrevs);
490 493 }
491 494
492 495 static PyObject *index_clearcaches(indexObject *self)
493 496 {
494 497 _index_clearcaches(self);
495 498 self->ntlength = self->ntcapacity = 0;
496 499 self->ntdepth = self->ntsplits = 0;
497 500 self->ntrev = -1;
498 501 self->ntlookups = self->ntmisses = 0;
499 502 Py_RETURN_NONE;
500 503 }
501 504
502 505 static PyObject *index_stats(indexObject *self)
503 506 {
504 507 PyObject *obj = PyDict_New();
505 508
506 509 if (obj == NULL)
507 510 return NULL;
508 511
509 512 #define istat(__n, __d) \
510 513 if (PyDict_SetItemString(obj, __d, PyInt_FromSsize_t(self->__n)) == -1) \
511 514 goto bail;
512 515
513 516 if (self->added) {
514 517 Py_ssize_t len = PyList_GET_SIZE(self->added);
515 518 if (PyDict_SetItemString(obj, "index entries added",
516 519 PyInt_FromSsize_t(len)) == -1)
517 520 goto bail;
518 521 }
519 522
520 523 if (self->raw_length != self->length - 1)
521 524 istat(raw_length, "revs on disk");
522 525 istat(length, "revs in memory");
523 526 istat(ntcapacity, "node trie capacity");
524 527 istat(ntdepth, "node trie depth");
525 528 istat(ntlength, "node trie count");
526 529 istat(ntlookups, "node trie lookups");
527 530 istat(ntmisses, "node trie misses");
528 531 istat(ntrev, "node trie last rev scanned");
529 532 istat(ntsplits, "node trie splits");
530 533
531 534 #undef istat
532 535
533 536 return obj;
534 537
535 538 bail:
536 539 Py_XDECREF(obj);
537 540 return NULL;
538 541 }
539 542
540 543 /*
541 544 * When we cache a list, we want to be sure the caller can't mutate
542 545 * the cached copy.
543 546 */
544 547 static PyObject *list_copy(PyObject *list)
545 548 {
546 549 Py_ssize_t len = PyList_GET_SIZE(list);
547 550 PyObject *newlist = PyList_New(len);
548 551 Py_ssize_t i;
549 552
550 553 if (newlist == NULL)
551 554 return NULL;
552 555
553 556 for (i = 0; i < len; i++) {
554 557 PyObject *obj = PyList_GET_ITEM(list, i);
555 558 Py_INCREF(obj);
556 559 PyList_SET_ITEM(newlist, i, obj);
557 560 }
558 561
559 562 return newlist;
560 563 }
561 564
562 565 static PyObject *index_headrevs(indexObject *self)
563 566 {
564 567 Py_ssize_t i, len, addlen;
565 568 char *nothead = NULL;
566 569 PyObject *heads;
567 570
568 571 if (self->headrevs)
569 572 return list_copy(self->headrevs);
570 573
571 574 len = index_length(self) - 1;
572 575 heads = PyList_New(0);
573 576 if (heads == NULL)
574 577 goto bail;
575 578 if (len == 0) {
576 579 PyObject *nullid = PyInt_FromLong(-1);
577 580 if (nullid == NULL || PyList_Append(heads, nullid) == -1) {
578 581 Py_XDECREF(nullid);
579 582 goto bail;
580 583 }
581 584 goto done;
582 585 }
583 586
584 587 nothead = calloc(len, 1);
585 588 if (nothead == NULL)
586 589 goto bail;
587 590
588 591 for (i = 0; i < self->raw_length; i++) {
589 592 const char *data = index_deref(self, i);
590 593 int parent_1 = getbe32(data + 24);
591 594 int parent_2 = getbe32(data + 28);
592 595 if (parent_1 >= 0)
593 596 nothead[parent_1] = 1;
594 597 if (parent_2 >= 0)
595 598 nothead[parent_2] = 1;
596 599 }
597 600
598 601 addlen = self->added ? PyList_GET_SIZE(self->added) : 0;
599 602
600 603 for (i = 0; i < addlen; i++) {
601 604 PyObject *rev = PyList_GET_ITEM(self->added, i);
602 605 PyObject *p1 = PyTuple_GET_ITEM(rev, 5);
603 606 PyObject *p2 = PyTuple_GET_ITEM(rev, 6);
604 607 long parent_1, parent_2;
605 608
606 609 if (!PyInt_Check(p1) || !PyInt_Check(p2)) {
607 610 PyErr_SetString(PyExc_TypeError,
608 611 "revlog parents are invalid");
609 612 goto bail;
610 613 }
611 614 parent_1 = PyInt_AS_LONG(p1);
612 615 parent_2 = PyInt_AS_LONG(p2);
613 616 if (parent_1 >= 0)
614 617 nothead[parent_1] = 1;
615 618 if (parent_2 >= 0)
616 619 nothead[parent_2] = 1;
617 620 }
618 621
619 622 for (i = 0; i < len; i++) {
620 623 PyObject *head;
621 624
622 625 if (nothead[i])
623 626 continue;
624 627 head = PyInt_FromLong(i);
625 628 if (head == NULL || PyList_Append(heads, head) == -1) {
626 629 Py_XDECREF(head);
627 630 goto bail;
628 631 }
629 632 }
630 633
631 634 done:
632 635 self->headrevs = heads;
633 636 free(nothead);
634 637 return list_copy(self->headrevs);
635 638 bail:
636 639 Py_XDECREF(heads);
637 640 free(nothead);
638 641 return NULL;
639 642 }
640 643
641 644 static inline int nt_level(const char *node, Py_ssize_t level)
642 645 {
643 646 int v = node[level>>1];
644 647 if (!(level & 1))
645 648 v >>= 4;
646 649 return v & 0xf;
647 650 }
648 651
649 652 /*
650 653 * Return values:
651 654 *
652 655 * -4: match is ambiguous (multiple candidates)
653 656 * -2: not found
654 657 * rest: valid rev
655 658 */
656 659 static int nt_find(indexObject *self, const char *node, Py_ssize_t nodelen,
657 660 int hex)
658 661 {
659 662 int (*getnybble)(const char *, Py_ssize_t) = hex ? hexdigit : nt_level;
660 663 int level, maxlevel, off;
661 664
662 665 if (nodelen == 20 && node[0] == '\0' && memcmp(node, nullid, 20) == 0)
663 666 return -1;
664 667
665 668 if (self->nt == NULL)
666 669 return -2;
667 670
668 671 if (hex)
669 672 maxlevel = nodelen > 40 ? 40 : (int)nodelen;
670 673 else
671 674 maxlevel = nodelen > 20 ? 40 : ((int)nodelen * 2);
672 675
673 676 for (level = off = 0; level < maxlevel; level++) {
674 677 int k = getnybble(node, level);
675 678 nodetree *n = &self->nt[off];
676 679 int v = n->children[k];
677 680
678 681 if (v < 0) {
679 682 const char *n;
680 683 Py_ssize_t i;
681 684
682 685 v = -v - 1;
683 686 n = index_node(self, v);
684 687 if (n == NULL)
685 688 return -2;
686 689 for (i = level; i < maxlevel; i++)
687 690 if (getnybble(node, i) != nt_level(n, i))
688 691 return -2;
689 692 return v;
690 693 }
691 694 if (v == 0)
692 695 return -2;
693 696 off = v;
694 697 }
695 698 /* multiple matches against an ambiguous prefix */
696 699 return -4;
697 700 }
698 701
699 702 static int nt_new(indexObject *self)
700 703 {
701 704 if (self->ntlength == self->ntcapacity) {
702 705 self->ntcapacity *= 2;
703 706 self->nt = realloc(self->nt,
704 707 self->ntcapacity * sizeof(nodetree));
705 708 if (self->nt == NULL) {
706 709 PyErr_SetString(PyExc_MemoryError, "out of memory");
707 710 return -1;
708 711 }
709 712 memset(&self->nt[self->ntlength], 0,
710 713 sizeof(nodetree) * (self->ntcapacity - self->ntlength));
711 714 }
712 715 return self->ntlength++;
713 716 }
714 717
715 718 static int nt_insert(indexObject *self, const char *node, int rev)
716 719 {
717 720 int level = 0;
718 721 int off = 0;
719 722
720 723 while (level < 40) {
721 724 int k = nt_level(node, level);
722 725 nodetree *n;
723 726 int v;
724 727
725 728 n = &self->nt[off];
726 729 v = n->children[k];
727 730
728 731 if (v == 0) {
729 732 n->children[k] = -rev - 1;
730 733 return 0;
731 734 }
732 735 if (v < 0) {
733 736 const char *oldnode = index_node(self, -v - 1);
734 737 int noff;
735 738
736 739 if (!oldnode || !memcmp(oldnode, node, 20)) {
737 740 n->children[k] = -rev - 1;
738 741 return 0;
739 742 }
740 743 noff = nt_new(self);
741 744 if (noff == -1)
742 745 return -1;
743 746 /* self->nt may have been changed by realloc */
744 747 self->nt[off].children[k] = noff;
745 748 off = noff;
746 749 n = &self->nt[off];
747 750 n->children[nt_level(oldnode, ++level)] = v;
748 751 if (level > self->ntdepth)
749 752 self->ntdepth = level;
750 753 self->ntsplits += 1;
751 754 } else {
752 755 level += 1;
753 756 off = v;
754 757 }
755 758 }
756 759
757 760 return -1;
758 761 }
759 762
760 763 static int nt_init(indexObject *self)
761 764 {
762 765 if (self->nt == NULL) {
763 766 self->ntcapacity = self->raw_length < 4
764 767 ? 4 : self->raw_length / 2;
765 768 self->nt = calloc(self->ntcapacity, sizeof(nodetree));
766 769 if (self->nt == NULL) {
767 770 PyErr_NoMemory();
768 771 return -1;
769 772 }
770 773 self->ntlength = 1;
771 774 self->ntrev = (int)index_length(self) - 1;
772 775 self->ntlookups = 1;
773 776 self->ntmisses = 0;
774 777 if (nt_insert(self, nullid, INT_MAX) == -1)
775 778 return -1;
776 779 }
777 780 return 0;
778 781 }
779 782
780 783 /*
781 784 * Return values:
782 785 *
783 786 * -3: error (exception set)
784 787 * -2: not found (no exception set)
785 788 * rest: valid rev
786 789 */
787 790 static int index_find_node(indexObject *self,
788 791 const char *node, Py_ssize_t nodelen)
789 792 {
790 793 int rev;
791 794
792 795 self->ntlookups++;
793 796 rev = nt_find(self, node, nodelen, 0);
794 797 if (rev >= -1)
795 798 return rev;
796 799
797 800 if (nt_init(self) == -1)
798 801 return -3;
799 802
800 803 /*
801 804 * For the first handful of lookups, we scan the entire index,
802 805 * and cache only the matching nodes. This optimizes for cases
803 806 * like "hg tip", where only a few nodes are accessed.
804 807 *
805 808 * After that, we cache every node we visit, using a single
806 809 * scan amortized over multiple lookups. This gives the best
807 810 * bulk performance, e.g. for "hg log".
808 811 */
809 812 if (self->ntmisses++ < 4) {
810 813 for (rev = self->ntrev - 1; rev >= 0; rev--) {
811 814 const char *n = index_node(self, rev);
812 815 if (n == NULL)
813 816 return -2;
814 817 if (memcmp(node, n, nodelen > 20 ? 20 : nodelen) == 0) {
815 818 if (nt_insert(self, n, rev) == -1)
816 819 return -3;
817 820 break;
818 821 }
819 822 }
820 823 } else {
821 824 for (rev = self->ntrev - 1; rev >= 0; rev--) {
822 825 const char *n = index_node(self, rev);
823 826 if (n == NULL) {
824 827 self->ntrev = rev + 1;
825 828 return -2;
826 829 }
827 830 if (nt_insert(self, n, rev) == -1) {
828 831 self->ntrev = rev + 1;
829 832 return -3;
830 833 }
831 834 if (memcmp(node, n, nodelen > 20 ? 20 : nodelen) == 0) {
832 835 break;
833 836 }
834 837 }
835 838 self->ntrev = rev;
836 839 }
837 840
838 841 if (rev >= 0)
839 842 return rev;
840 843 return -2;
841 844 }
842 845
843 846 static PyObject *raise_revlog_error(void)
844 847 {
845 848 static PyObject *errclass;
846 849 PyObject *mod = NULL, *errobj;
847 850
848 851 if (errclass == NULL) {
849 852 PyObject *dict;
850 853
851 854 mod = PyImport_ImportModule("mercurial.error");
852 855 if (mod == NULL)
853 856 goto classfail;
854 857
855 858 dict = PyModule_GetDict(mod);
856 859 if (dict == NULL)
857 860 goto classfail;
858 861
859 862 errclass = PyDict_GetItemString(dict, "RevlogError");
860 863 if (errclass == NULL) {
861 864 PyErr_SetString(PyExc_SystemError,
862 865 "could not find RevlogError");
863 866 goto classfail;
864 867 }
865 868 Py_INCREF(errclass);
866 869 }
867 870
868 871 errobj = PyObject_CallFunction(errclass, NULL);
869 872 if (errobj == NULL)
870 873 return NULL;
871 874 PyErr_SetObject(errclass, errobj);
872 875 return errobj;
873 876
874 877 classfail:
875 878 Py_XDECREF(mod);
876 879 return NULL;
877 880 }
878 881
879 882 static PyObject *index_getitem(indexObject *self, PyObject *value)
880 883 {
881 884 char *node;
882 885 Py_ssize_t nodelen;
883 886 int rev;
884 887
885 888 if (PyInt_Check(value))
886 889 return index_get(self, PyInt_AS_LONG(value));
887 890
888 891 if (node_check(value, &node, &nodelen) == -1)
889 892 return NULL;
890 893 rev = index_find_node(self, node, nodelen);
891 894 if (rev >= -1)
892 895 return PyInt_FromLong(rev);
893 896 if (rev == -2)
894 897 raise_revlog_error();
895 898 return NULL;
896 899 }
897 900
898 901 static int nt_partialmatch(indexObject *self, const char *node,
899 902 Py_ssize_t nodelen)
900 903 {
901 904 int rev;
902 905
903 906 if (nt_init(self) == -1)
904 907 return -3;
905 908
906 909 if (self->ntrev > 0) {
907 910 /* ensure that the radix tree is fully populated */
908 911 for (rev = self->ntrev - 1; rev >= 0; rev--) {
909 912 const char *n = index_node(self, rev);
910 913 if (n == NULL)
911 914 return -2;
912 915 if (nt_insert(self, n, rev) == -1)
913 916 return -3;
914 917 }
915 918 self->ntrev = rev;
916 919 }
917 920
918 921 return nt_find(self, node, nodelen, 1);
919 922 }
920 923
921 924 static PyObject *index_partialmatch(indexObject *self, PyObject *args)
922 925 {
923 926 const char *fullnode;
924 927 int nodelen;
925 928 char *node;
926 929 int rev, i;
927 930
928 931 if (!PyArg_ParseTuple(args, "s#", &node, &nodelen))
929 932 return NULL;
930 933
931 934 if (nodelen < 4) {
932 935 PyErr_SetString(PyExc_ValueError, "key too short");
933 936 return NULL;
934 937 }
935 938
936 939 if (nodelen > 40)
937 940 nodelen = 40;
938 941
939 942 for (i = 0; i < nodelen; i++)
940 943 hexdigit(node, i);
941 944 if (PyErr_Occurred()) {
942 945 /* input contains non-hex characters */
943 946 PyErr_Clear();
944 947 Py_RETURN_NONE;
945 948 }
946 949
947 950 rev = nt_partialmatch(self, node, nodelen);
948 951
949 952 switch (rev) {
950 953 case -4:
951 954 raise_revlog_error();
952 955 case -3:
953 956 return NULL;
954 957 case -2:
955 958 Py_RETURN_NONE;
956 959 case -1:
957 960 return PyString_FromStringAndSize(nullid, 20);
958 961 }
959 962
960 963 fullnode = index_node(self, rev);
961 964 if (fullnode == NULL) {
962 965 PyErr_Format(PyExc_IndexError,
963 966 "could not access rev %d", rev);
964 967 return NULL;
965 968 }
966 969 return PyString_FromStringAndSize(fullnode, 20);
967 970 }
968 971
969 972 static PyObject *index_m_get(indexObject *self, PyObject *args)
970 973 {
971 974 Py_ssize_t nodelen;
972 975 PyObject *val;
973 976 char *node;
974 977 int rev;
975 978
976 979 if (!PyArg_ParseTuple(args, "O", &val))
977 980 return NULL;
978 981 if (node_check(val, &node, &nodelen) == -1)
979 982 return NULL;
980 983 rev = index_find_node(self, node, nodelen);
981 984 if (rev == -3)
982 985 return NULL;
983 986 if (rev == -2)
984 987 Py_RETURN_NONE;
985 988 return PyInt_FromLong(rev);
986 989 }
987 990
988 991 static int index_contains(indexObject *self, PyObject *value)
989 992 {
990 993 char *node;
991 994 Py_ssize_t nodelen;
992 995
993 996 if (PyInt_Check(value)) {
994 997 long rev = PyInt_AS_LONG(value);
995 998 return rev >= -1 && rev < index_length(self);
996 999 }
997 1000
998 1001 if (node_check(value, &node, &nodelen) == -1)
999 1002 return -1;
1000 1003
1001 1004 switch (index_find_node(self, node, nodelen)) {
1002 1005 case -3:
1003 1006 return -1;
1004 1007 case -2:
1005 1008 return 0;
1006 1009 default:
1007 1010 return 1;
1008 1011 }
1009 1012 }
1010 1013
1011 1014 /*
1012 1015 * Invalidate any trie entries introduced by added revs.
1013 1016 */
1014 1017 static void nt_invalidate_added(indexObject *self, Py_ssize_t start)
1015 1018 {
1016 1019 Py_ssize_t i, len = PyList_GET_SIZE(self->added);
1017 1020
1018 1021 for (i = start; i < len; i++) {
1019 1022 PyObject *tuple = PyList_GET_ITEM(self->added, i);
1020 1023 PyObject *node = PyTuple_GET_ITEM(tuple, 7);
1021 1024
1022 1025 nt_insert(self, PyString_AS_STRING(node), -1);
1023 1026 }
1024 1027
1025 1028 if (start == 0)
1026 1029 Py_CLEAR(self->added);
1027 1030 }
1028 1031
1029 1032 /*
1030 1033 * Delete a numeric range of revs, which must be at the end of the
1031 1034 * range, but exclude the sentinel nullid entry.
1032 1035 */
1033 1036 static int index_slice_del(indexObject *self, PyObject *item)
1034 1037 {
1035 1038 Py_ssize_t start, stop, step, slicelength;
1036 1039 Py_ssize_t length = index_length(self);
1037 1040 int ret = 0;
1038 1041
1039 1042 if (PySlice_GetIndicesEx((PySliceObject*)item, length,
1040 1043 &start, &stop, &step, &slicelength) < 0)
1041 1044 return -1;
1042 1045
1043 1046 if (slicelength <= 0)
1044 1047 return 0;
1045 1048
1046 1049 if ((step < 0 && start < stop) || (step > 0 && start > stop))
1047 1050 stop = start;
1048 1051
1049 1052 if (step < 0) {
1050 1053 stop = start + 1;
1051 1054 start = stop + step*(slicelength - 1) - 1;
1052 1055 step = -step;
1053 1056 }
1054 1057
1055 1058 if (step != 1) {
1056 1059 PyErr_SetString(PyExc_ValueError,
1057 1060 "revlog index delete requires step size of 1");
1058 1061 return -1;
1059 1062 }
1060 1063
1061 1064 if (stop != length - 1) {
1062 1065 PyErr_SetString(PyExc_IndexError,
1063 1066 "revlog index deletion indices are invalid");
1064 1067 return -1;
1065 1068 }
1066 1069
1067 1070 if (start < self->length - 1) {
1068 1071 if (self->nt) {
1069 1072 Py_ssize_t i;
1070 1073
1071 1074 for (i = start + 1; i < self->length - 1; i++) {
1072 1075 const char *node = index_node(self, i);
1073 1076
1074 1077 if (node)
1075 1078 nt_insert(self, node, -1);
1076 1079 }
1077 1080 if (self->added)
1078 1081 nt_invalidate_added(self, 0);
1079 1082 if (self->ntrev > start)
1080 1083 self->ntrev = (int)start;
1081 1084 }
1082 1085 self->length = start + 1;
1083 1086 if (start < self->raw_length)
1084 1087 self->raw_length = start;
1085 1088 goto done;
1086 1089 }
1087 1090
1088 1091 if (self->nt) {
1089 1092 nt_invalidate_added(self, start - self->length + 1);
1090 1093 if (self->ntrev > start)
1091 1094 self->ntrev = (int)start;
1092 1095 }
1093 1096 if (self->added)
1094 1097 ret = PyList_SetSlice(self->added, start - self->length + 1,
1095 1098 PyList_GET_SIZE(self->added), NULL);
1096 1099 done:
1097 1100 Py_CLEAR(self->headrevs);
1098 1101 return ret;
1099 1102 }
1100 1103
1101 1104 /*
1102 1105 * Supported ops:
1103 1106 *
1104 1107 * slice deletion
1105 1108 * string assignment (extend node->rev mapping)
1106 1109 * string deletion (shrink node->rev mapping)
1107 1110 */
1108 1111 static int index_assign_subscript(indexObject *self, PyObject *item,
1109 1112 PyObject *value)
1110 1113 {
1111 1114 char *node;
1112 1115 Py_ssize_t nodelen;
1113 1116 long rev;
1114 1117
1115 1118 if (PySlice_Check(item) && value == NULL)
1116 1119 return index_slice_del(self, item);
1117 1120
1118 1121 if (node_check(item, &node, &nodelen) == -1)
1119 1122 return -1;
1120 1123
1121 1124 if (value == NULL)
1122 1125 return self->nt ? nt_insert(self, node, -1) : 0;
1123 1126 rev = PyInt_AsLong(value);
1124 1127 if (rev > INT_MAX || rev < 0) {
1125 1128 if (!PyErr_Occurred())
1126 1129 PyErr_SetString(PyExc_ValueError, "rev out of range");
1127 1130 return -1;
1128 1131 }
1129 1132 return nt_insert(self, node, (int)rev);
1130 1133 }
1131 1134
1132 1135 /*
1133 1136 * Find all RevlogNG entries in an index that has inline data. Update
1134 1137 * the optional "offsets" table with those entries.
1135 1138 */
1136 1139 static long inline_scan(indexObject *self, const char **offsets)
1137 1140 {
1138 1141 const char *data = PyString_AS_STRING(self->data);
1139 1142 const char *end = data + PyString_GET_SIZE(self->data);
1140 const long hdrsize = 64;
1141 long incr = hdrsize;
1143 long incr = v1_hdrsize;
1142 1144 Py_ssize_t len = 0;
1143 1145
1144 while (data + hdrsize <= end) {
1146 while (data + v1_hdrsize <= end) {
1145 1147 uint32_t comp_len;
1146 1148 const char *old_data;
1147 1149 /* 3rd element of header is length of compressed inline data */
1148 1150 comp_len = getbe32(data + 8);
1149 incr = hdrsize + comp_len;
1150 if (incr < hdrsize)
1151 incr = v1_hdrsize + comp_len;
1152 if (incr < v1_hdrsize)
1151 1153 break;
1152 1154 if (offsets)
1153 1155 offsets[len] = data;
1154 1156 len++;
1155 1157 old_data = data;
1156 1158 data += incr;
1157 1159 if (data <= old_data)
1158 1160 break;
1159 1161 }
1160 1162
1161 if (data != end && data + hdrsize != end) {
1163 if (data != end && data + v1_hdrsize != end) {
1162 1164 if (!PyErr_Occurred())
1163 1165 PyErr_SetString(PyExc_ValueError, "corrupt index file");
1164 1166 return -1;
1165 1167 }
1166 1168
1167 1169 return len;
1168 1170 }
1169 1171
1170 1172 static int index_init(indexObject *self, PyObject *args)
1171 1173 {
1172 1174 PyObject *data_obj, *inlined_obj;
1173 1175 Py_ssize_t size;
1174 1176
1175 1177 if (!PyArg_ParseTuple(args, "OO", &data_obj, &inlined_obj))
1176 1178 return -1;
1177 1179 if (!PyString_Check(data_obj)) {
1178 1180 PyErr_SetString(PyExc_TypeError, "data is not a string");
1179 1181 return -1;
1180 1182 }
1181 1183 size = PyString_GET_SIZE(data_obj);
1182 1184
1183 1185 self->inlined = inlined_obj && PyObject_IsTrue(inlined_obj);
1184 1186 self->data = data_obj;
1185 1187 self->cache = NULL;
1186 1188
1187 1189 self->added = NULL;
1188 1190 self->headrevs = NULL;
1189 1191 self->offsets = NULL;
1190 1192 self->nt = NULL;
1191 1193 self->ntlength = self->ntcapacity = 0;
1192 1194 self->ntdepth = self->ntsplits = 0;
1193 1195 self->ntlookups = self->ntmisses = 0;
1194 1196 self->ntrev = -1;
1195 1197 Py_INCREF(self->data);
1196 1198
1197 1199 if (self->inlined) {
1198 1200 long len = inline_scan(self, NULL);
1199 1201 if (len == -1)
1200 1202 goto bail;
1201 1203 self->raw_length = len;
1202 1204 self->length = len + 1;
1203 1205 } else {
1204 if (size % 64) {
1206 if (size % v1_hdrsize) {
1205 1207 PyErr_SetString(PyExc_ValueError, "corrupt index file");
1206 1208 goto bail;
1207 1209 }
1208 self->raw_length = size / 64;
1210 self->raw_length = size / v1_hdrsize;
1209 1211 self->length = self->raw_length + 1;
1210 1212 }
1211 1213
1212 1214 return 0;
1213 1215 bail:
1214 1216 return -1;
1215 1217 }
1216 1218
1217 1219 static PyObject *index_nodemap(indexObject *self)
1218 1220 {
1219 1221 Py_INCREF(self);
1220 1222 return (PyObject *)self;
1221 1223 }
1222 1224
1223 1225 static void index_dealloc(indexObject *self)
1224 1226 {
1225 1227 _index_clearcaches(self);
1226 1228 Py_DECREF(self->data);
1227 1229 Py_XDECREF(self->added);
1228 1230 PyObject_Del(self);
1229 1231 }
1230 1232
1231 1233 static PySequenceMethods index_sequence_methods = {
1232 1234 (lenfunc)index_length, /* sq_length */
1233 1235 0, /* sq_concat */
1234 1236 0, /* sq_repeat */
1235 1237 (ssizeargfunc)index_get, /* sq_item */
1236 1238 0, /* sq_slice */
1237 1239 0, /* sq_ass_item */
1238 1240 0, /* sq_ass_slice */
1239 1241 (objobjproc)index_contains, /* sq_contains */
1240 1242 };
1241 1243
1242 1244 static PyMappingMethods index_mapping_methods = {
1243 1245 (lenfunc)index_length, /* mp_length */
1244 1246 (binaryfunc)index_getitem, /* mp_subscript */
1245 1247 (objobjargproc)index_assign_subscript, /* mp_ass_subscript */
1246 1248 };
1247 1249
1248 1250 static PyMethodDef index_methods[] = {
1249 1251 {"clearcaches", (PyCFunction)index_clearcaches, METH_NOARGS,
1250 1252 "clear the index caches"},
1251 1253 {"get", (PyCFunction)index_m_get, METH_VARARGS,
1252 1254 "get an index entry"},
1253 1255 {"headrevs", (PyCFunction)index_headrevs, METH_NOARGS,
1254 1256 "get head revisions"},
1255 1257 {"insert", (PyCFunction)index_insert, METH_VARARGS,
1256 1258 "insert an index entry"},
1257 1259 {"partialmatch", (PyCFunction)index_partialmatch, METH_VARARGS,
1258 1260 "match a potentially ambiguous node ID"},
1259 1261 {"stats", (PyCFunction)index_stats, METH_NOARGS,
1260 1262 "stats for the index"},
1261 1263 {NULL} /* Sentinel */
1262 1264 };
1263 1265
1264 1266 static PyGetSetDef index_getset[] = {
1265 1267 {"nodemap", (getter)index_nodemap, NULL, "nodemap", NULL},
1266 1268 {NULL} /* Sentinel */
1267 1269 };
1268 1270
1269 1271 static PyTypeObject indexType = {
1270 1272 PyObject_HEAD_INIT(NULL)
1271 1273 0, /* ob_size */
1272 1274 "parsers.index", /* tp_name */
1273 1275 sizeof(indexObject), /* tp_basicsize */
1274 1276 0, /* tp_itemsize */
1275 1277 (destructor)index_dealloc, /* tp_dealloc */
1276 1278 0, /* tp_print */
1277 1279 0, /* tp_getattr */
1278 1280 0, /* tp_setattr */
1279 1281 0, /* tp_compare */
1280 1282 0, /* tp_repr */
1281 1283 0, /* tp_as_number */
1282 1284 &index_sequence_methods, /* tp_as_sequence */
1283 1285 &index_mapping_methods, /* tp_as_mapping */
1284 1286 0, /* tp_hash */
1285 1287 0, /* tp_call */
1286 1288 0, /* tp_str */
1287 1289 0, /* tp_getattro */
1288 1290 0, /* tp_setattro */
1289 1291 0, /* tp_as_buffer */
1290 1292 Py_TPFLAGS_DEFAULT, /* tp_flags */
1291 1293 "revlog index", /* tp_doc */
1292 1294 0, /* tp_traverse */
1293 1295 0, /* tp_clear */
1294 1296 0, /* tp_richcompare */
1295 1297 0, /* tp_weaklistoffset */
1296 1298 0, /* tp_iter */
1297 1299 0, /* tp_iternext */
1298 1300 index_methods, /* tp_methods */
1299 1301 0, /* tp_members */
1300 1302 index_getset, /* tp_getset */
1301 1303 0, /* tp_base */
1302 1304 0, /* tp_dict */
1303 1305 0, /* tp_descr_get */
1304 1306 0, /* tp_descr_set */
1305 1307 0, /* tp_dictoffset */
1306 1308 (initproc)index_init, /* tp_init */
1307 1309 0, /* tp_alloc */
1308 1310 };
1309 1311
1310 1312 /*
1311 1313 * returns a tuple of the form (index, index, cache) with elements as
1312 1314 * follows:
1313 1315 *
1314 1316 * index: an index object that lazily parses RevlogNG records
1315 1317 * cache: if data is inlined, a tuple (index_file_content, 0), else None
1316 1318 *
1317 1319 * added complications are for backwards compatibility
1318 1320 */
1319 1321 static PyObject *parse_index2(PyObject *self, PyObject *args)
1320 1322 {
1321 1323 PyObject *tuple = NULL, *cache = NULL;
1322 1324 indexObject *idx;
1323 1325 int ret;
1324 1326
1325 1327 idx = PyObject_New(indexObject, &indexType);
1326 1328 if (idx == NULL)
1327 1329 goto bail;
1328 1330
1329 1331 ret = index_init(idx, args);
1330 1332 if (ret == -1)
1331 1333 goto bail;
1332 1334
1333 1335 if (idx->inlined) {
1334 1336 cache = Py_BuildValue("iO", 0, idx->data);
1335 1337 if (cache == NULL)
1336 1338 goto bail;
1337 1339 } else {
1338 1340 cache = Py_None;
1339 1341 Py_INCREF(cache);
1340 1342 }
1341 1343
1342 1344 tuple = Py_BuildValue("NN", idx, cache);
1343 1345 if (!tuple)
1344 1346 goto bail;
1345 1347 return tuple;
1346 1348
1347 1349 bail:
1348 1350 Py_XDECREF(idx);
1349 1351 Py_XDECREF(cache);
1350 1352 Py_XDECREF(tuple);
1351 1353 return NULL;
1352 1354 }
1353 1355
1354 1356 static char parsers_doc[] = "Efficient content parsing.";
1355 1357
1356 1358 static PyMethodDef methods[] = {
1357 1359 {"parse_manifest", parse_manifest, METH_VARARGS, "parse a manifest\n"},
1358 1360 {"parse_dirstate", parse_dirstate, METH_VARARGS, "parse a dirstate\n"},
1359 1361 {"parse_index2", parse_index2, METH_VARARGS, "parse a revlog index\n"},
1360 1362 {NULL, NULL}
1361 1363 };
1362 1364
1363 1365 static void module_init(PyObject *mod)
1364 1366 {
1365 1367 indexType.tp_new = PyType_GenericNew;
1366 1368 if (PyType_Ready(&indexType) < 0)
1367 1369 return;
1368 1370 Py_INCREF(&indexType);
1369 1371
1370 1372 PyModule_AddObject(mod, "index", (PyObject *)&indexType);
1371 1373
1372 1374 nullentry = Py_BuildValue("iiiiiiis#", 0, 0, 0,
1373 1375 -1, -1, -1, -1, nullid, 20);
1374 1376 if (nullentry)
1375 1377 PyObject_GC_UnTrack(nullentry);
1376 1378 }
1377 1379
1378 1380 #ifdef IS_PY3K
1379 1381 static struct PyModuleDef parsers_module = {
1380 1382 PyModuleDef_HEAD_INIT,
1381 1383 "parsers",
1382 1384 parsers_doc,
1383 1385 -1,
1384 1386 methods
1385 1387 };
1386 1388
1387 1389 PyMODINIT_FUNC PyInit_parsers(void)
1388 1390 {
1389 1391 PyObject *mod = PyModule_Create(&parsers_module);
1390 1392 module_init(mod);
1391 1393 return mod;
1392 1394 }
1393 1395 #else
1394 1396 PyMODINIT_FUNC initparsers(void)
1395 1397 {
1396 1398 PyObject *mod = Py_InitModule3("parsers", methods, parsers_doc);
1397 1399 module_init(mod);
1398 1400 }
1399 1401 #endif
General Comments 0
You need to be logged in to leave comments. Login now