##// END OF EJS Templates
parsers: incrementally parse the revlog index in C...
Bryan O'Sullivan -
r16363:2cdd7e63 default
parent child Browse files
Show More
@@ -1,418 +1,720 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 int hexdigit(char c)
17 17 {
18 18 if (c >= '0' && c <= '9')
19 19 return c - '0';
20 20 if (c >= 'a' && c <= 'f')
21 21 return c - 'a' + 10;
22 22 if (c >= 'A' && c <= 'F')
23 23 return c - 'A' + 10;
24 24
25 25 PyErr_SetString(PyExc_ValueError, "input contains non-hex character");
26 26 return 0;
27 27 }
28 28
29 29 /*
30 30 * Turn a hex-encoded string into binary.
31 31 */
32 32 static PyObject *unhexlify(const char *str, int len)
33 33 {
34 34 PyObject *ret;
35 35 const char *c;
36 36 char *d;
37 37
38 38 ret = PyBytes_FromStringAndSize(NULL, len / 2);
39 39
40 40 if (!ret)
41 41 return NULL;
42 42
43 43 d = PyBytes_AsString(ret);
44 44
45 45 for (c = str; c < str + len;) {
46 46 int hi = hexdigit(*c++);
47 47 int lo = hexdigit(*c++);
48 48 *d++ = (hi << 4) | lo;
49 49 }
50 50
51 51 return ret;
52 52 }
53 53
54 54 /*
55 55 * This code assumes that a manifest is stitched together with newline
56 56 * ('\n') characters.
57 57 */
58 58 static PyObject *parse_manifest(PyObject *self, PyObject *args)
59 59 {
60 60 PyObject *mfdict, *fdict;
61 61 char *str, *cur, *start, *zero;
62 62 int len;
63 63
64 64 if (!PyArg_ParseTuple(args, "O!O!s#:parse_manifest",
65 65 &PyDict_Type, &mfdict,
66 66 &PyDict_Type, &fdict,
67 67 &str, &len))
68 68 goto quit;
69 69
70 70 for (start = cur = str, zero = NULL; cur < str + len; cur++) {
71 71 PyObject *file = NULL, *node = NULL;
72 72 PyObject *flags = NULL;
73 73 int nlen;
74 74
75 75 if (!*cur) {
76 76 zero = cur;
77 77 continue;
78 78 }
79 79 else if (*cur != '\n')
80 80 continue;
81 81
82 82 if (!zero) {
83 83 PyErr_SetString(PyExc_ValueError,
84 84 "manifest entry has no separator");
85 85 goto quit;
86 86 }
87 87
88 88 file = PyBytes_FromStringAndSize(start, zero - start);
89 89
90 90 if (!file)
91 91 goto bail;
92 92
93 93 nlen = cur - zero - 1;
94 94
95 95 node = unhexlify(zero + 1, nlen > 40 ? 40 : nlen);
96 96 if (!node)
97 97 goto bail;
98 98
99 99 if (nlen > 40) {
100 100 flags = PyBytes_FromStringAndSize(zero + 41,
101 101 nlen - 40);
102 102 if (!flags)
103 103 goto bail;
104 104
105 105 if (PyDict_SetItem(fdict, file, flags) == -1)
106 106 goto bail;
107 107 }
108 108
109 109 if (PyDict_SetItem(mfdict, file, node) == -1)
110 110 goto bail;
111 111
112 112 start = cur + 1;
113 113 zero = NULL;
114 114
115 115 Py_XDECREF(flags);
116 116 Py_XDECREF(node);
117 117 Py_XDECREF(file);
118 118 continue;
119 119 bail:
120 120 Py_XDECREF(flags);
121 121 Py_XDECREF(node);
122 122 Py_XDECREF(file);
123 123 goto quit;
124 124 }
125 125
126 126 if (len > 0 && *(cur - 1) != '\n') {
127 127 PyErr_SetString(PyExc_ValueError,
128 128 "manifest contains trailing garbage");
129 129 goto quit;
130 130 }
131 131
132 132 Py_INCREF(Py_None);
133 133 return Py_None;
134 134 quit:
135 135 return NULL;
136 136 }
137 137
138 138 #ifdef _WIN32
139 139 #ifdef _MSC_VER
140 140 /* msvc 6.0 has problems */
141 141 #define inline __inline
142 142 typedef unsigned long uint32_t;
143 143 typedef unsigned __int64 uint64_t;
144 144 #else
145 145 #include <stdint.h>
146 146 #endif
147 147 static uint32_t ntohl(uint32_t x)
148 148 {
149 149 return ((x & 0x000000ffUL) << 24) |
150 150 ((x & 0x0000ff00UL) << 8) |
151 151 ((x & 0x00ff0000UL) >> 8) |
152 152 ((x & 0xff000000UL) >> 24);
153 153 }
154 154 #else
155 155 /* not windows */
156 156 #include <sys/types.h>
157 157 #if defined __BEOS__ && !defined __HAIKU__
158 158 #include <ByteOrder.h>
159 159 #else
160 160 #include <arpa/inet.h>
161 161 #endif
162 162 #include <inttypes.h>
163 163 #endif
164 164
165 165 static PyObject *parse_dirstate(PyObject *self, PyObject *args)
166 166 {
167 167 PyObject *dmap, *cmap, *parents = NULL, *ret = NULL;
168 168 PyObject *fname = NULL, *cname = NULL, *entry = NULL;
169 169 char *str, *cur, *end, *cpos;
170 170 int state, mode, size, mtime;
171 171 unsigned int flen;
172 172 int len;
173 173 uint32_t decode[4]; /* for alignment */
174 174
175 175 if (!PyArg_ParseTuple(args, "O!O!s#:parse_dirstate",
176 176 &PyDict_Type, &dmap,
177 177 &PyDict_Type, &cmap,
178 178 &str, &len))
179 179 goto quit;
180 180
181 181 /* read parents */
182 182 if (len < 40)
183 183 goto quit;
184 184
185 185 parents = Py_BuildValue("s#s#", str, 20, str + 20, 20);
186 186 if (!parents)
187 187 goto quit;
188 188
189 189 /* read filenames */
190 190 cur = str + 40;
191 191 end = str + len;
192 192
193 193 while (cur < end - 17) {
194 194 /* unpack header */
195 195 state = *cur;
196 196 memcpy(decode, cur + 1, 16);
197 197 mode = ntohl(decode[0]);
198 198 size = ntohl(decode[1]);
199 199 mtime = ntohl(decode[2]);
200 200 flen = ntohl(decode[3]);
201 201 cur += 17;
202 202 if (cur + flen > end || cur + flen < cur) {
203 203 PyErr_SetString(PyExc_ValueError, "overflow in dirstate");
204 204 goto quit;
205 205 }
206 206
207 207 entry = Py_BuildValue("ciii", state, mode, size, mtime);
208 208 if (!entry)
209 209 goto quit;
210 210 PyObject_GC_UnTrack(entry); /* don't waste time with this */
211 211
212 212 cpos = memchr(cur, 0, flen);
213 213 if (cpos) {
214 214 fname = PyBytes_FromStringAndSize(cur, cpos - cur);
215 215 cname = PyBytes_FromStringAndSize(cpos + 1,
216 216 flen - (cpos - cur) - 1);
217 217 if (!fname || !cname ||
218 218 PyDict_SetItem(cmap, fname, cname) == -1 ||
219 219 PyDict_SetItem(dmap, fname, entry) == -1)
220 220 goto quit;
221 221 Py_DECREF(cname);
222 222 } else {
223 223 fname = PyBytes_FromStringAndSize(cur, flen);
224 224 if (!fname ||
225 225 PyDict_SetItem(dmap, fname, entry) == -1)
226 226 goto quit;
227 227 }
228 228 cur += flen;
229 229 Py_DECREF(fname);
230 230 Py_DECREF(entry);
231 231 fname = cname = entry = NULL;
232 232 }
233 233
234 234 ret = parents;
235 235 Py_INCREF(ret);
236 236 quit:
237 237 Py_XDECREF(fname);
238 238 Py_XDECREF(cname);
239 239 Py_XDECREF(entry);
240 240 Py_XDECREF(parents);
241 241 return ret;
242 242 }
243 243
244 const char nullid[20];
245 const int nullrev = -1;
244 /*
245 * A list-like object that decodes the contents of a RevlogNG index
246 * file on demand. It has limited support for insert and delete at the
247 * last element before the end. The last entry is always a sentinel
248 * nullid.
249 */
250 typedef struct {
251 PyObject_HEAD
252 /* Type-specific fields go here. */
253 PyObject *data; /* raw bytes of index */
254 PyObject **cache; /* cached tuples */
255 const char **offsets; /* populated on demand */
256 Py_ssize_t raw_length; /* original number of elements */
257 Py_ssize_t length; /* current number of elements */
258 PyObject *added; /* populated on demand */
259 int inlined;
260 } indexObject;
261
262 static Py_ssize_t index_length(indexObject *self)
263 {
264 if (self->added == NULL)
265 return self->length;
266 return self->length + PyList_GET_SIZE(self->added);
267 }
268
269 static PyObject *nullentry;
270
271 static long inline_scan(indexObject *self, const char **offsets);
272
273 #if LONG_MAX == 0x7fffffffL
274 static const char *tuple_format = "Kiiiiiis#";
275 #else
276 static const char *tuple_format = "kiiiiiis#";
277 #endif
246 278
247 279 /* RevlogNG format (all in big endian, data may be inlined):
248 280 * 6 bytes: offset
249 281 * 2 bytes: flags
250 282 * 4 bytes: compressed length
251 283 * 4 bytes: uncompressed length
252 284 * 4 bytes: base revision
253 285 * 4 bytes: link revision
254 286 * 4 bytes: parent 1 revision
255 287 * 4 bytes: parent 2 revision
256 288 * 32 bytes: nodeid (only 20 bytes used)
257 289 */
258 static int _parse_index_ng(const char *data, int size, int inlined,
259 PyObject *index)
290 static PyObject *index_get(indexObject *self, Py_ssize_t pos)
260 291 {
261 PyObject *entry;
262 int n = 0, err;
292 uint32_t decode[8]; /* to enforce alignment with inline data */
263 293 uint64_t offset_flags;
264 294 int comp_len, uncomp_len, base_rev, link_rev, parent_1, parent_2;
265 295 const char *c_node_id;
266 const char *end = data + size;
267 uint32_t decode[8]; /* to enforce alignment with inline data */
296 const char *data;
297 Py_ssize_t length = index_length(self);
298 PyObject *entry;
299
300 if (pos >= length) {
301 PyErr_SetString(PyExc_IndexError, "revlog index out of range");
302 return NULL;
303 }
304
305 if (pos == length - 1) {
306 Py_INCREF(nullentry);
307 return nullentry;
308 }
309
310 if (pos >= self->length - 1) {
311 PyObject *obj;
312 obj = PyList_GET_ITEM(self->added, pos - self->length + 1);
313 Py_INCREF(obj);
314 return obj;
315 }
268 316
269 while (data < end) {
270 unsigned int step;
317 if (self->cache) {
318 if (self->cache[pos]) {
319 Py_INCREF(self->cache[pos]);
320 return self->cache[pos];
321 }
322 } else {
323 self->cache = calloc(self->raw_length, sizeof(PyObject *));
324 if (self->cache == NULL)
325 return PyErr_NoMemory();
326 }
271 327
272 memcpy(decode, data, 32);
328 if (self->inlined && pos > 0) {
329 if (self->offsets == NULL) {
330 self->offsets = malloc(self->raw_length *
331 sizeof(*self->offsets));
332 if (self->offsets == NULL)
333 return PyErr_NoMemory();
334 inline_scan(self, self->offsets);
335 }
336 data = self->offsets[pos];
337 } else
338 data = PyString_AS_STRING(self->data) + pos * 64;
339
340 memcpy(decode, data, 8 * sizeof(uint32_t));
341
273 342 offset_flags = ntohl(decode[1]);
274 if (n == 0) /* mask out version number for the first entry */
343 if (pos == 0) /* mask out version number for the first entry */
275 344 offset_flags &= 0xFFFF;
276 345 else {
277 346 uint32_t offset_high = ntohl(decode[0]);
278 347 offset_flags |= ((uint64_t)offset_high) << 32;
279 348 }
280 349
281 350 comp_len = ntohl(decode[2]);
282 351 uncomp_len = ntohl(decode[3]);
283 352 base_rev = ntohl(decode[4]);
284 353 link_rev = ntohl(decode[5]);
285 354 parent_1 = ntohl(decode[6]);
286 355 parent_2 = ntohl(decode[7]);
287 356 c_node_id = data + 32;
288 357
289 entry = Py_BuildValue("Liiiiiis#", offset_flags, comp_len,
358 entry = Py_BuildValue(tuple_format, offset_flags, comp_len,
290 359 uncomp_len, base_rev, link_rev,
291 360 parent_1, parent_2, c_node_id, 20);
292 361
293 if (!entry)
362 if (entry)
363 PyObject_GC_UnTrack(entry);
364
365 self->cache[pos] = entry;
366 Py_INCREF(entry);
367
368 return entry;
369 }
370
371 static PyObject *index_insert(indexObject *self, PyObject *args)
372 {
373 PyObject *obj, *node;
374 long offset;
375 Py_ssize_t len;
376
377 if (!PyArg_ParseTuple(args, "lO", &offset, &obj))
378 return NULL;
379
380 if (!PyTuple_Check(obj) || PyTuple_GET_SIZE(obj) != 8) {
381 PyErr_SetString(PyExc_ValueError, "8-tuple required");
382 return NULL;
383 }
384
385 node = PyTuple_GET_ITEM(obj, 7);
386 if (!PyString_Check(node) || PyString_GET_SIZE(node) != 20) {
387 PyErr_SetString(PyExc_ValueError,
388 "20-byte hash required as last element");
389 return NULL;
390 }
391
392 len = index_length(self);
393
394 if (offset < 0)
395 offset += len;
396
397 if (offset != len - 1) {
398 PyErr_SetString(PyExc_IndexError,
399 "insert only supported at index -1");
400 return NULL;
401 }
402
403 if (self->added == NULL) {
404 self->added = PyList_New(0);
405 if (self->added == NULL)
406 return NULL;
407 }
408
409 if (PyList_Append(self->added, obj) == -1)
410 return NULL;
411
412 Py_RETURN_NONE;
413 }
414
415 static int index_assign_subscript(indexObject *self, PyObject *item,
416 PyObject *value)
417 {
418 Py_ssize_t start, stop, step, slicelength;
419 Py_ssize_t length = index_length(self);
420
421 if (!PySlice_Check(item) || value != NULL) {
422 PyErr_SetString(PyExc_TypeError,
423 "revlog index only supports slice deletion");
424 return -1;
425 }
426
427 if (PySlice_GetIndicesEx((PySliceObject*)item, length,
428 &start, &stop, &step, &slicelength) < 0)
429 return -1;
430
431 if (slicelength <= 0)
294 432 return 0;
295 433
296 PyObject_GC_UnTrack(entry); /* don't waste time with this */
434 if ((step < 0 && start < stop) || (step > 0 && start > stop))
435 stop = start;
297 436
298 if (inlined) {
299 err = PyList_Append(index, entry);
300 Py_DECREF(entry);
301 if (err)
302 return 0;
303 } else
304 PyList_SET_ITEM(index, n, entry); /* steals reference */
437 if (step < 0) {
438 stop = start + 1;
439 start = stop + step*(slicelength - 1) - 1;
440 step = -step;
441 }
305 442
306 n++;
307 step = 64 + (inlined ? comp_len : 0);
308 if (data + step > end || data + step < data)
309 break;
310 data += step;
443 if (step != 1) {
444 PyErr_SetString(PyExc_ValueError,
445 "revlog index delete requires step size of 1");
446 return -1;
311 447 }
312 if (data != end) {
313 if (!PyErr_Occurred())
314 PyErr_SetString(PyExc_ValueError, "corrupt index file");
448
449 if (stop != length - 1) {
450 PyErr_SetString(PyExc_IndexError,
451 "revlog index deletion indices are invalid");
452 return -1;
453 }
454
455 if (start < self->length) {
456 self->length = start + 1;
457 if (self->added) {
458 Py_DECREF(self->added);
459 self->added = NULL;
460 }
315 461 return 0;
316 462 }
317 463
318 /* create the magic nullid entry in the index at [-1] */
319 entry = Py_BuildValue("Liiiiiis#", (uint64_t)0, 0, 0, -1, -1, -1, -1, nullid, 20);
464 return PyList_SetSlice(self->added, start - self->length + 1,
465 PyList_GET_SIZE(self->added),
466 NULL);
467 }
468
469 static long inline_scan(indexObject *self, const char **offsets)
470 {
471 const char *data = PyString_AS_STRING(self->data);
472 const char *end = data + PyString_GET_SIZE(self->data);
473 const long hdrsize = 64;
474 long incr = hdrsize;
475 Py_ssize_t len = 0;
320 476
321 if (!entry)
322 return 0;
477 while (data + hdrsize <= end) {
478 uint32_t comp_len;
479 const char *old_data;
480 /* 3rd element of header is length of compressed inline data */
481 memcpy(&comp_len, data + 8, sizeof(uint32_t));
482 incr = hdrsize + ntohl(comp_len);
483 if (incr < hdrsize)
484 break;
485 if (offsets)
486 offsets[len] = data;
487 len++;
488 old_data = data;
489 data += incr;
490 if (data <= old_data)
491 break;
492 }
323 493
324 PyObject_GC_UnTrack(entry); /* don't waste time with this */
494 if (data != end && data + hdrsize != end) {
495 if (!PyErr_Occurred())
496 PyErr_SetString(PyExc_ValueError, "corrupt index file");
497 return -1;
498 }
499
500 return len;
501 }
325 502
326 if (inlined) {
327 err = PyList_Append(index, entry);
328 Py_DECREF(entry);
329 if (err)
503 static int index_real_init(indexObject *self, const char *data, int size,
504 PyObject *inlined_obj, PyObject *data_obj)
505 {
506 self->inlined = inlined_obj && PyObject_IsTrue(inlined_obj);
507 self->data = data_obj;
508 self->cache = NULL;
509
510 self->added = NULL;
511 self->offsets = NULL;
512 Py_INCREF(self->data);
513
514 if (self->inlined) {
515 long len = inline_scan(self, NULL);
516 if (len == -1)
517 goto bail;
518 self->raw_length = len;
519 self->length = len + 1;
520 } else {
521 if (size % 64) {
522 PyErr_SetString(PyExc_ValueError, "corrupt index file");
523 goto bail;
524 }
525 self->raw_length = size / 64;
526 self->length = self->raw_length + 1;
527 }
528
330 529 return 0;
331 } else
332 PyList_SET_ITEM(index, n, entry); /* steals reference */
530 bail:
531 return -1;
532 }
333 533
334 return 1;
534 static int index_init(indexObject *self, PyObject *args, PyObject *kwds)
535 {
536 const char *data;
537 int size;
538 PyObject *inlined_obj;
539
540 if (!PyArg_ParseTuple(args, "s#O", &data, &size, &inlined_obj))
541 return -1;
542
543 return index_real_init(self, data, size, inlined_obj,
544 PyTuple_GET_ITEM(args, 0));
335 545 }
336 546
337 /* This function parses a index file and returns a Python tuple of the
338 * following format: (index, cache)
547 static void index_dealloc(indexObject *self)
548 {
549 Py_DECREF(self->data);
550 if (self->cache) {
551 Py_ssize_t i;
552
553 for (i = 0; i < self->raw_length; i++)
554 Py_XDECREF(self->cache[i]);
555 }
556 Py_XDECREF(self->added);
557 free(self->offsets);
558 PyObject_Del(self);
559 }
560
561 static PySequenceMethods index_sequence_methods = {
562 (lenfunc)index_length, /* sq_length */
563 0, /* sq_concat */
564 0, /* sq_repeat */
565 (ssizeargfunc)index_get, /* sq_item */
566 };
567
568 static PyMappingMethods index_mapping_methods = {
569 (lenfunc)index_length, /* mp_length */
570 NULL, /* mp_subscript */
571 (objobjargproc)index_assign_subscript, /* mp_ass_subscript */
572 };
573
574 static PyMethodDef index_methods[] = {
575 {"insert", (PyCFunction)index_insert, METH_VARARGS,
576 "insert an index entry"},
577 {NULL} /* Sentinel */
578 };
579
580 static PyTypeObject indexType = {
581 PyObject_HEAD_INIT(NULL)
582 0, /* ob_size */
583 "parsers.index", /* tp_name */
584 sizeof(indexObject), /* tp_basicsize */
585 0, /* tp_itemsize */
586 (destructor)index_dealloc, /* tp_dealloc */
587 0, /* tp_print */
588 0, /* tp_getattr */
589 0, /* tp_setattr */
590 0, /* tp_compare */
591 0, /* tp_repr */
592 0, /* tp_as_number */
593 &index_sequence_methods, /* tp_as_sequence */
594 &index_mapping_methods, /* tp_as_mapping */
595 0, /* tp_hash */
596 0, /* tp_call */
597 0, /* tp_str */
598 0, /* tp_getattro */
599 0, /* tp_setattro */
600 0, /* tp_as_buffer */
601 Py_TPFLAGS_DEFAULT, /* tp_flags */
602 "revlog index", /* tp_doc */
603 0, /* tp_traverse */
604 0, /* tp_clear */
605 0, /* tp_richcompare */
606 0, /* tp_weaklistoffset */
607 0, /* tp_iter */
608 0, /* tp_iternext */
609 index_methods, /* tp_methods */
610 0, /* tp_members */
611 0, /* tp_getset */
612 0, /* tp_base */
613 0, /* tp_dict */
614 0, /* tp_descr_get */
615 0, /* tp_descr_set */
616 0, /* tp_dictoffset */
617 (initproc)index_init, /* tp_init */
618 0, /* tp_alloc */
619 PyType_GenericNew, /* tp_new */
620 };
621
622 /*
623 * returns a tuple of the form (index, None, cache) with elements as
624 * follows:
339 625 *
340 * index: a list of tuples containing the RevlogNG records
341 * cache: if data is inlined, a tuple (index_file_content, 0) else None
626 * index: an index object that lazily parses the RevlogNG records
627 * cache: if data is inlined, a tuple (index_file_content, 0), else None
628 *
629 * added complications are for backwards compatibility
342 630 */
343 631 static PyObject *parse_index2(PyObject *self, PyObject *args)
344 632 {
345 633 const char *data;
346 int size, inlined;
347 PyObject *rval = NULL, *index = NULL, *cache = NULL;
348 PyObject *data_obj = NULL, *inlined_obj;
634 int size, ret;
635 PyObject *inlined_obj, *tuple = NULL, *cache = NULL;
636 indexObject *idx;
349 637
350 638 if (!PyArg_ParseTuple(args, "s#O", &data, &size, &inlined_obj))
351 639 return NULL;
352 inlined = inlined_obj && PyObject_IsTrue(inlined_obj);
640
641 idx = PyObject_New(indexObject, &indexType);
642
643 if (idx == NULL)
644 goto bail;
353 645
354 /* If no data is inlined, we know the size of the index list in
355 * advance: size divided by the size of one revlog record (64 bytes)
356 * plus one for nullid */
357 index = inlined ? PyList_New(0) : PyList_New(size / 64 + 1);
358 if (!index)
359 goto quit;
646 ret = index_real_init(idx, data, size, inlined_obj,
647 PyTuple_GET_ITEM(args, 0));
648 if (ret)
649 goto bail;
360 650
361 /* set up the cache return value */
362 if (inlined) {
363 /* Note that the reference to data_obj is only borrowed */
364 data_obj = PyTuple_GET_ITEM(args, 0);
365 cache = Py_BuildValue("iO", 0, data_obj);
366 if (!cache)
367 goto quit;
651 if (idx->inlined) {
652 Py_INCREF(idx->data);
653 cache = Py_BuildValue("iO", 0, idx->data);
654 if (cache == NULL)
655 goto bail;
368 656 } else {
369 657 cache = Py_None;
370 Py_INCREF(Py_None);
658 Py_INCREF(cache);
371 659 }
372 660
373 /* actually populate the index with data */
374 if (!_parse_index_ng(data, size, inlined, index))
375 goto quit;
661 tuple = Py_BuildValue("NN", idx, cache);
662 if (!tuple)
663 goto bail;
664 return tuple;
376 665
377 rval = Py_BuildValue("NN", index, cache);
378 if (!rval)
379 goto quit;
380 return rval;
381
382 quit:
383 Py_XDECREF(index);
666 bail:
667 Py_XDECREF(idx);
384 668 Py_XDECREF(cache);
385 Py_XDECREF(rval);
669 Py_XDECREF(tuple);
386 670 return NULL;
387 671 }
388 672
389
390 673 static char parsers_doc[] = "Efficient content parsing.";
391 674
392 675 static PyMethodDef methods[] = {
393 676 {"parse_manifest", parse_manifest, METH_VARARGS, "parse a manifest\n"},
394 677 {"parse_dirstate", parse_dirstate, METH_VARARGS, "parse a dirstate\n"},
395 678 {"parse_index2", parse_index2, METH_VARARGS, "parse a revlog index\n"},
396 679 {NULL, NULL}
397 680 };
398 681
682 static void module_init(PyObject *mod)
683 {
684 static const char nullid[20];
685
686 if (PyType_Ready(&indexType) < 0)
687 return;
688 Py_INCREF(&indexType);
689
690 PyModule_AddObject(mod, "index", (PyObject *)&indexType);
691
692 nullentry = Py_BuildValue("iiiiiiis#", 0, 0, 0,
693 -1, -1, -1, -1, nullid, 20);
694 if (nullentry)
695 PyObject_GC_UnTrack(nullentry);
696 }
697
399 698 #ifdef IS_PY3K
400 699 static struct PyModuleDef parsers_module = {
401 700 PyModuleDef_HEAD_INIT,
402 701 "parsers",
403 702 parsers_doc,
404 703 -1,
405 704 methods
406 705 };
407 706
408 707 PyMODINIT_FUNC PyInit_parsers(void)
409 708 {
410 return PyModule_Create(&parsers_module);
709 PyObject *mod = PyModule_Create(&parsers_module);
710 module_init(mod);
711 return mod;
411 712 }
412 713 #else
413 714 PyMODINIT_FUNC initparsers(void)
414 715 {
415 Py_InitModule3("parsers", methods, parsers_doc);
716 PyObject *mod = Py_InitModule3("parsers", methods, parsers_doc);
717 module_init(mod);
416 718 }
417 719 #endif
418 720
@@ -1,113 +1,115 b''
1 1 from mercurial import parsers
2 2 from mercurial.node import nullid, nullrev
3 3 import struct
4 4
5 5 # This unit test compares the return value of the original Python
6 6 # implementation of parseindex and the new C implementation for
7 7 # an index file with and without inlined data
8 8
9 9 # original python implementation
10 10 def gettype(q):
11 11 return int(q & 0xFFFF)
12 12
13 13 def offset_type(offset, type):
14 14 return long(long(offset) << 16 | type)
15 15
16 16 indexformatng = ">Qiiiiii20s12x"
17 17
18 18 def py_parseindex(data, inline) :
19 19 s = 64
20 20 cache = None
21 21 index = []
22 22 nodemap = {nullid: nullrev}
23 23 n = off = 0
24 24
25 25 l = len(data) - s
26 26 append = index.append
27 27 if inline:
28 28 cache = (0, data)
29 29 while off <= l:
30 30 e = struct.unpack(indexformatng, data[off:off + s])
31 31 nodemap[e[7]] = n
32 32 append(e)
33 33 n += 1
34 34 if e[1] < 0:
35 35 break
36 36 off += e[1] + s
37 37 else:
38 38 while off <= l:
39 39 e = struct.unpack(indexformatng, data[off:off + s])
40 40 nodemap[e[7]] = n
41 41 append(e)
42 42 n += 1
43 43 off += s
44 44
45 45 e = list(index[0])
46 46 type = gettype(e[0])
47 47 e[0] = offset_type(0, type)
48 48 index[0] = tuple(e)
49 49
50 50 # add the magic null revision at -1
51 51 index.append((0, 0, 0, -1, -1, -1, -1, nullid))
52 52
53 53 return index, cache
54 54
55
56 55 data_inlined = '\x00\x01\x00\x01\x00\x00\x00\x00\x00\x00\x01\x8c' \
57 56 '\x00\x00\x04\x07\x00\x00\x00\x00\x00\x00\x15\x15\xff\xff\xff' \
58 57 '\xff\xff\xff\xff\xff\xebG\x97\xb7\x1fB\x04\xcf\x13V\x81\tw\x1b' \
59 58 'w\xdduR\xda\xc6\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' \
60 59 'x\x9c\x9d\x93?O\xc30\x10\xc5\xf7|\x8a\xdb\x9a\xa8m\x06\xd8*\x95' \
61 60 '\x81B\xa1\xa2\xa2R\xcb\x86Pd\x9a\x0b5$vd_\x04\xfd\xf6\x9c\xff@' \
62 61 '\x11!\x0b\xd9\xec\xf7\xbbw\xe7gG6\xad6\x04\xdaN\xc0\x92\xa0$)' \
63 62 '\xb1\x82\xa2\xd1%\x16\xa4\x8b7\xa9\xca\xd4-\xb2Y\x02\xfc\xc9' \
64 63 '\xcaS\xf9\xaeX\xed\xb6\xd77Q\x02\x83\xd4\x19\xf5--Y\xea\xe1W' \
65 64 '\xab\xed\x10\xceR\x0f_\xdf\xdf\r\xe1,\xf5\xf0\xcb\xf5 \xceR\x0f' \
66 65 '_\xdc\x0e\x0e\xc3R\x0f_\xae\x96\x9b!\x9e\xa5\x1e\xbf\xdb,\x06' \
67 66 '\xc7q\x9a/\x88\x82\xc3B\xea\xb5\xb4TJ\x93\xb6\x82\x0e\xe16\xe6' \
68 67 'KQ\xdb\xaf\xecG\xa3\xd1 \x01\xd3\x0b_^\xe8\xaa\xa0\xae\xad\xd1' \
69 68 '&\xbef\x1bz\x08\xb0|\xc9Xz\x06\xf6Z\x91\x90J\xaa\x17\x90\xaa' \
70 69 '\xd2\xa6\x11$5C\xcf\xba#\xa0\x03\x02*2\x92-\xfc\xb1\x94\xdf\xe2' \
71 70 '\xae\xb8\'m\x8ey0^\x85\xd3\x82\xb4\xf0`:\x9c\x00\x8a\xfd\x01' \
72 71 '\xb0\xc6\x86\x8b\xdd\xae\x80\xf3\xa9\x9fd\x16\n\x00R%\x1a\x06' \
73 72 '\xe9\xd8b\x98\x1d\xf4\xf3+\x9bf\x01\xd8p\x1b\xf3.\xed\x9f^g\xc3' \
74 73 '^\xd9W81T\xdb\xd5\x04sx|\xf2\xeb\xd6`%?x\xed"\x831\xbf\xf3\xdc' \
75 74 'b\xeb%gaY\xe1\xad\x9f\xb9f\'1w\xa9\xa5a\x83s\x82J\xb98\xbc4\x8b' \
76 75 '\x83\x00\x9f$z\xb8#\xa5\xb1\xdf\x98\xd9\xec\x1b\x89O\xe3Ts\x9a4' \
77 76 '\x17m\x8b\xfc\x8f\xa5\x95\x9a\xfc\xfa\xed,\xe5|\xa1\xfe\x15\xb9' \
78 77 '\xbc\xb2\x93\x1f\xf2\x95\xff\xdf,\x1a\xc5\xe7\x17*\x93Oz:>\x0e'
79 78
80 79 data_non_inlined = '\x00\x00\x00\x01\x00\x00\x00\x00\x00\x01D\x19' \
81 80 '\x00\x07e\x12\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff' \
82 81 '\xff\xff\xff\xff\xd1\xf4\xbb\xb0\xbe\xfc\x13\xbd\x8c\xd3\x9d' \
83 82 '\x0f\xcd\xd9;\x8c\x07\x8cJ/\x00\x00\x00\x00\x00\x00\x00\x00\x00' \
84 83 '\x00\x00\x00\x00\x00\x00\x01D\x19\x00\x00\x00\x00\x00\xdf\x00' \
85 84 '\x00\x01q\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\xff' \
86 85 '\xff\xff\xff\xc1\x12\xb9\x04\x96\xa4Z1t\x91\xdfsJ\x90\xf0\x9bh' \
87 86 '\x07l&\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' \
88 87 '\x00\x01D\xf8\x00\x00\x00\x00\x01\x1b\x00\x00\x01\xb8\x00\x00' \
89 88 '\x00\x01\x00\x00\x00\x02\x00\x00\x00\x01\xff\xff\xff\xff\x02\n' \
90 89 '\x0e\xc6&\xa1\x92\xae6\x0b\x02i\xfe-\xe5\xbao\x05\xd1\xe7\x00' \
91 90 '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01F' \
92 91 '\x13\x00\x00\x00\x00\x01\xec\x00\x00\x03\x06\x00\x00\x00\x01' \
93 92 '\x00\x00\x00\x03\x00\x00\x00\x02\xff\xff\xff\xff\x12\xcb\xeby1' \
94 93 '\xb6\r\x98B\xcb\x07\xbd`\x8f\x92\xd9\xc4\x84\xbdK\x00\x00\x00' \
95 94 '\x00\x00\x00\x00\x00\x00\x00\x00\x00'
96 95
97 def runtest() :
96 def parse_index2(data, inline):
97 index, chunkcache = parsers.parse_index2(data, inline)
98 return list(index), chunkcache
98 99
100 def runtest() :
99 101 py_res_1 = py_parseindex(data_inlined, True)
100 c_res_1 = parsers.parse_index2(data_inlined, True)
102 c_res_1 = parse_index2(data_inlined, True)
101 103
102 104 py_res_2 = py_parseindex(data_non_inlined, False)
103 c_res_2 = parsers.parse_index2(data_non_inlined, False)
105 c_res_2 = parse_index2(data_non_inlined, False)
104 106
105 107 if py_res_1 != c_res_1:
106 108 print "Parse index result (with inlined data) differs!"
107 109
108 110 if py_res_2 != c_res_2:
109 111 print "Parse index result (no inlined data) differs!"
110 112
111 113 print "done"
112 114
113 115 runtest()
General Comments 0
You need to be logged in to leave comments. Login now