##// 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 parsers.c - efficient content parsing
2 parsers.c - efficient content parsing
3
3
4 Copyright 2008 Matt Mackall <mpm@selenic.com> and others
4 Copyright 2008 Matt Mackall <mpm@selenic.com> and others
5
5
6 This software may be used and distributed according to the terms of
6 This software may be used and distributed according to the terms of
7 the GNU General Public License, incorporated herein by reference.
7 the GNU General Public License, incorporated herein by reference.
8 */
8 */
9
9
10 #include <Python.h>
10 #include <Python.h>
11 #include <ctype.h>
11 #include <ctype.h>
12 #include <string.h>
12 #include <string.h>
13
13
14 #include "util.h"
14 #include "util.h"
15
15
16 static int hexdigit(char c)
16 static int hexdigit(char c)
17 {
17 {
18 if (c >= '0' && c <= '9')
18 if (c >= '0' && c <= '9')
19 return c - '0';
19 return c - '0';
20 if (c >= 'a' && c <= 'f')
20 if (c >= 'a' && c <= 'f')
21 return c - 'a' + 10;
21 return c - 'a' + 10;
22 if (c >= 'A' && c <= 'F')
22 if (c >= 'A' && c <= 'F')
23 return c - 'A' + 10;
23 return c - 'A' + 10;
24
24
25 PyErr_SetString(PyExc_ValueError, "input contains non-hex character");
25 PyErr_SetString(PyExc_ValueError, "input contains non-hex character");
26 return 0;
26 return 0;
27 }
27 }
28
28
29 /*
29 /*
30 * Turn a hex-encoded string into binary.
30 * Turn a hex-encoded string into binary.
31 */
31 */
32 static PyObject *unhexlify(const char *str, int len)
32 static PyObject *unhexlify(const char *str, int len)
33 {
33 {
34 PyObject *ret;
34 PyObject *ret;
35 const char *c;
35 const char *c;
36 char *d;
36 char *d;
37
37
38 ret = PyBytes_FromStringAndSize(NULL, len / 2);
38 ret = PyBytes_FromStringAndSize(NULL, len / 2);
39
39
40 if (!ret)
40 if (!ret)
41 return NULL;
41 return NULL;
42
42
43 d = PyBytes_AsString(ret);
43 d = PyBytes_AsString(ret);
44
44
45 for (c = str; c < str + len;) {
45 for (c = str; c < str + len;) {
46 int hi = hexdigit(*c++);
46 int hi = hexdigit(*c++);
47 int lo = hexdigit(*c++);
47 int lo = hexdigit(*c++);
48 *d++ = (hi << 4) | lo;
48 *d++ = (hi << 4) | lo;
49 }
49 }
50
50
51 return ret;
51 return ret;
52 }
52 }
53
53
54 /*
54 /*
55 * This code assumes that a manifest is stitched together with newline
55 * This code assumes that a manifest is stitched together with newline
56 * ('\n') characters.
56 * ('\n') characters.
57 */
57 */
58 static PyObject *parse_manifest(PyObject *self, PyObject *args)
58 static PyObject *parse_manifest(PyObject *self, PyObject *args)
59 {
59 {
60 PyObject *mfdict, *fdict;
60 PyObject *mfdict, *fdict;
61 char *str, *cur, *start, *zero;
61 char *str, *cur, *start, *zero;
62 int len;
62 int len;
63
63
64 if (!PyArg_ParseTuple(args, "O!O!s#:parse_manifest",
64 if (!PyArg_ParseTuple(args, "O!O!s#:parse_manifest",
65 &PyDict_Type, &mfdict,
65 &PyDict_Type, &mfdict,
66 &PyDict_Type, &fdict,
66 &PyDict_Type, &fdict,
67 &str, &len))
67 &str, &len))
68 goto quit;
68 goto quit;
69
69
70 for (start = cur = str, zero = NULL; cur < str + len; cur++) {
70 for (start = cur = str, zero = NULL; cur < str + len; cur++) {
71 PyObject *file = NULL, *node = NULL;
71 PyObject *file = NULL, *node = NULL;
72 PyObject *flags = NULL;
72 PyObject *flags = NULL;
73 int nlen;
73 int nlen;
74
74
75 if (!*cur) {
75 if (!*cur) {
76 zero = cur;
76 zero = cur;
77 continue;
77 continue;
78 }
78 }
79 else if (*cur != '\n')
79 else if (*cur != '\n')
80 continue;
80 continue;
81
81
82 if (!zero) {
82 if (!zero) {
83 PyErr_SetString(PyExc_ValueError,
83 PyErr_SetString(PyExc_ValueError,
84 "manifest entry has no separator");
84 "manifest entry has no separator");
85 goto quit;
85 goto quit;
86 }
86 }
87
87
88 file = PyBytes_FromStringAndSize(start, zero - start);
88 file = PyBytes_FromStringAndSize(start, zero - start);
89
89
90 if (!file)
90 if (!file)
91 goto bail;
91 goto bail;
92
92
93 nlen = cur - zero - 1;
93 nlen = cur - zero - 1;
94
94
95 node = unhexlify(zero + 1, nlen > 40 ? 40 : nlen);
95 node = unhexlify(zero + 1, nlen > 40 ? 40 : nlen);
96 if (!node)
96 if (!node)
97 goto bail;
97 goto bail;
98
98
99 if (nlen > 40) {
99 if (nlen > 40) {
100 flags = PyBytes_FromStringAndSize(zero + 41,
100 flags = PyBytes_FromStringAndSize(zero + 41,
101 nlen - 40);
101 nlen - 40);
102 if (!flags)
102 if (!flags)
103 goto bail;
103 goto bail;
104
104
105 if (PyDict_SetItem(fdict, file, flags) == -1)
105 if (PyDict_SetItem(fdict, file, flags) == -1)
106 goto bail;
106 goto bail;
107 }
107 }
108
108
109 if (PyDict_SetItem(mfdict, file, node) == -1)
109 if (PyDict_SetItem(mfdict, file, node) == -1)
110 goto bail;
110 goto bail;
111
111
112 start = cur + 1;
112 start = cur + 1;
113 zero = NULL;
113 zero = NULL;
114
114
115 Py_XDECREF(flags);
115 Py_XDECREF(flags);
116 Py_XDECREF(node);
116 Py_XDECREF(node);
117 Py_XDECREF(file);
117 Py_XDECREF(file);
118 continue;
118 continue;
119 bail:
119 bail:
120 Py_XDECREF(flags);
120 Py_XDECREF(flags);
121 Py_XDECREF(node);
121 Py_XDECREF(node);
122 Py_XDECREF(file);
122 Py_XDECREF(file);
123 goto quit;
123 goto quit;
124 }
124 }
125
125
126 if (len > 0 && *(cur - 1) != '\n') {
126 if (len > 0 && *(cur - 1) != '\n') {
127 PyErr_SetString(PyExc_ValueError,
127 PyErr_SetString(PyExc_ValueError,
128 "manifest contains trailing garbage");
128 "manifest contains trailing garbage");
129 goto quit;
129 goto quit;
130 }
130 }
131
131
132 Py_INCREF(Py_None);
132 Py_INCREF(Py_None);
133 return Py_None;
133 return Py_None;
134 quit:
134 quit:
135 return NULL;
135 return NULL;
136 }
136 }
137
137
138 #ifdef _WIN32
138 #ifdef _WIN32
139 #ifdef _MSC_VER
139 #ifdef _MSC_VER
140 /* msvc 6.0 has problems */
140 /* msvc 6.0 has problems */
141 #define inline __inline
141 #define inline __inline
142 typedef unsigned long uint32_t;
142 typedef unsigned long uint32_t;
143 typedef unsigned __int64 uint64_t;
143 typedef unsigned __int64 uint64_t;
144 #else
144 #else
145 #include <stdint.h>
145 #include <stdint.h>
146 #endif
146 #endif
147 static uint32_t ntohl(uint32_t x)
147 static uint32_t ntohl(uint32_t x)
148 {
148 {
149 return ((x & 0x000000ffUL) << 24) |
149 return ((x & 0x000000ffUL) << 24) |
150 ((x & 0x0000ff00UL) << 8) |
150 ((x & 0x0000ff00UL) << 8) |
151 ((x & 0x00ff0000UL) >> 8) |
151 ((x & 0x00ff0000UL) >> 8) |
152 ((x & 0xff000000UL) >> 24);
152 ((x & 0xff000000UL) >> 24);
153 }
153 }
154 #else
154 #else
155 /* not windows */
155 /* not windows */
156 #include <sys/types.h>
156 #include <sys/types.h>
157 #if defined __BEOS__ && !defined __HAIKU__
157 #if defined __BEOS__ && !defined __HAIKU__
158 #include <ByteOrder.h>
158 #include <ByteOrder.h>
159 #else
159 #else
160 #include <arpa/inet.h>
160 #include <arpa/inet.h>
161 #endif
161 #endif
162 #include <inttypes.h>
162 #include <inttypes.h>
163 #endif
163 #endif
164
164
165 static PyObject *parse_dirstate(PyObject *self, PyObject *args)
165 static PyObject *parse_dirstate(PyObject *self, PyObject *args)
166 {
166 {
167 PyObject *dmap, *cmap, *parents = NULL, *ret = NULL;
167 PyObject *dmap, *cmap, *parents = NULL, *ret = NULL;
168 PyObject *fname = NULL, *cname = NULL, *entry = NULL;
168 PyObject *fname = NULL, *cname = NULL, *entry = NULL;
169 char *str, *cur, *end, *cpos;
169 char *str, *cur, *end, *cpos;
170 int state, mode, size, mtime;
170 int state, mode, size, mtime;
171 unsigned int flen;
171 unsigned int flen;
172 int len;
172 int len;
173 uint32_t decode[4]; /* for alignment */
173 uint32_t decode[4]; /* for alignment */
174
174
175 if (!PyArg_ParseTuple(args, "O!O!s#:parse_dirstate",
175 if (!PyArg_ParseTuple(args, "O!O!s#:parse_dirstate",
176 &PyDict_Type, &dmap,
176 &PyDict_Type, &dmap,
177 &PyDict_Type, &cmap,
177 &PyDict_Type, &cmap,
178 &str, &len))
178 &str, &len))
179 goto quit;
179 goto quit;
180
180
181 /* read parents */
181 /* read parents */
182 if (len < 40)
182 if (len < 40)
183 goto quit;
183 goto quit;
184
184
185 parents = Py_BuildValue("s#s#", str, 20, str + 20, 20);
185 parents = Py_BuildValue("s#s#", str, 20, str + 20, 20);
186 if (!parents)
186 if (!parents)
187 goto quit;
187 goto quit;
188
188
189 /* read filenames */
189 /* read filenames */
190 cur = str + 40;
190 cur = str + 40;
191 end = str + len;
191 end = str + len;
192
192
193 while (cur < end - 17) {
193 while (cur < end - 17) {
194 /* unpack header */
194 /* unpack header */
195 state = *cur;
195 state = *cur;
196 memcpy(decode, cur + 1, 16);
196 memcpy(decode, cur + 1, 16);
197 mode = ntohl(decode[0]);
197 mode = ntohl(decode[0]);
198 size = ntohl(decode[1]);
198 size = ntohl(decode[1]);
199 mtime = ntohl(decode[2]);
199 mtime = ntohl(decode[2]);
200 flen = ntohl(decode[3]);
200 flen = ntohl(decode[3]);
201 cur += 17;
201 cur += 17;
202 if (cur + flen > end || cur + flen < cur) {
202 if (cur + flen > end || cur + flen < cur) {
203 PyErr_SetString(PyExc_ValueError, "overflow in dirstate");
203 PyErr_SetString(PyExc_ValueError, "overflow in dirstate");
204 goto quit;
204 goto quit;
205 }
205 }
206
206
207 entry = Py_BuildValue("ciii", state, mode, size, mtime);
207 entry = Py_BuildValue("ciii", state, mode, size, mtime);
208 if (!entry)
208 if (!entry)
209 goto quit;
209 goto quit;
210 PyObject_GC_UnTrack(entry); /* don't waste time with this */
210 PyObject_GC_UnTrack(entry); /* don't waste time with this */
211
211
212 cpos = memchr(cur, 0, flen);
212 cpos = memchr(cur, 0, flen);
213 if (cpos) {
213 if (cpos) {
214 fname = PyBytes_FromStringAndSize(cur, cpos - cur);
214 fname = PyBytes_FromStringAndSize(cur, cpos - cur);
215 cname = PyBytes_FromStringAndSize(cpos + 1,
215 cname = PyBytes_FromStringAndSize(cpos + 1,
216 flen - (cpos - cur) - 1);
216 flen - (cpos - cur) - 1);
217 if (!fname || !cname ||
217 if (!fname || !cname ||
218 PyDict_SetItem(cmap, fname, cname) == -1 ||
218 PyDict_SetItem(cmap, fname, cname) == -1 ||
219 PyDict_SetItem(dmap, fname, entry) == -1)
219 PyDict_SetItem(dmap, fname, entry) == -1)
220 goto quit;
220 goto quit;
221 Py_DECREF(cname);
221 Py_DECREF(cname);
222 } else {
222 } else {
223 fname = PyBytes_FromStringAndSize(cur, flen);
223 fname = PyBytes_FromStringAndSize(cur, flen);
224 if (!fname ||
224 if (!fname ||
225 PyDict_SetItem(dmap, fname, entry) == -1)
225 PyDict_SetItem(dmap, fname, entry) == -1)
226 goto quit;
226 goto quit;
227 }
227 }
228 cur += flen;
228 cur += flen;
229 Py_DECREF(fname);
229 Py_DECREF(fname);
230 Py_DECREF(entry);
230 Py_DECREF(entry);
231 fname = cname = entry = NULL;
231 fname = cname = entry = NULL;
232 }
232 }
233
233
234 ret = parents;
234 ret = parents;
235 Py_INCREF(ret);
235 Py_INCREF(ret);
236 quit:
236 quit:
237 Py_XDECREF(fname);
237 Py_XDECREF(fname);
238 Py_XDECREF(cname);
238 Py_XDECREF(cname);
239 Py_XDECREF(entry);
239 Py_XDECREF(entry);
240 Py_XDECREF(parents);
240 Py_XDECREF(parents);
241 return ret;
241 return ret;
242 }
242 }
243
243
244 const char nullid[20];
244 /*
245 const int nullrev = -1;
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 /* RevlogNG format (all in big endian, data may be inlined):
279 /* RevlogNG format (all in big endian, data may be inlined):
248 * 6 bytes: offset
280 * 6 bytes: offset
249 * 2 bytes: flags
281 * 2 bytes: flags
250 * 4 bytes: compressed length
282 * 4 bytes: compressed length
251 * 4 bytes: uncompressed length
283 * 4 bytes: uncompressed length
252 * 4 bytes: base revision
284 * 4 bytes: base revision
253 * 4 bytes: link revision
285 * 4 bytes: link revision
254 * 4 bytes: parent 1 revision
286 * 4 bytes: parent 1 revision
255 * 4 bytes: parent 2 revision
287 * 4 bytes: parent 2 revision
256 * 32 bytes: nodeid (only 20 bytes used)
288 * 32 bytes: nodeid (only 20 bytes used)
257 */
289 */
258 static int _parse_index_ng(const char *data, int size, int inlined,
290 static PyObject *index_get(indexObject *self, Py_ssize_t pos)
259 PyObject *index)
260 {
291 {
261 PyObject *entry;
292 uint32_t decode[8]; /* to enforce alignment with inline data */
262 int n = 0, err;
263 uint64_t offset_flags;
293 uint64_t offset_flags;
264 int comp_len, uncomp_len, base_rev, link_rev, parent_1, parent_2;
294 int comp_len, uncomp_len, base_rev, link_rev, parent_1, parent_2;
265 const char *c_node_id;
295 const char *c_node_id;
266 const char *end = data + size;
296 const char *data;
267 uint32_t decode[8]; /* to enforce alignment with inline 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 }
268
309
269 while (data < end) {
310 if (pos >= self->length - 1) {
270 unsigned int step;
311 PyObject *obj;
312 obj = PyList_GET_ITEM(self->added, pos - self->length + 1);
313 Py_INCREF(obj);
314 return obj;
315 }
316
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) {
273 offset_flags = ntohl(decode[1]);
329 if (self->offsets == NULL) {
274 if (n == 0) /* mask out version number for the first entry */
330 self->offsets = malloc(self->raw_length *
275 offset_flags &= 0xFFFF;
331 sizeof(*self->offsets));
276 else {
332 if (self->offsets == NULL)
277 uint32_t offset_high = ntohl(decode[0]);
333 return PyErr_NoMemory();
278 offset_flags |= ((uint64_t)offset_high) << 32;
334 inline_scan(self, self->offsets);
279 }
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));
280
341
281 comp_len = ntohl(decode[2]);
342 offset_flags = ntohl(decode[1]);
282 uncomp_len = ntohl(decode[3]);
343 if (pos == 0) /* mask out version number for the first entry */
283 base_rev = ntohl(decode[4]);
344 offset_flags &= 0xFFFF;
284 link_rev = ntohl(decode[5]);
345 else {
285 parent_1 = ntohl(decode[6]);
346 uint32_t offset_high = ntohl(decode[0]);
286 parent_2 = ntohl(decode[7]);
347 offset_flags |= ((uint64_t)offset_high) << 32;
287 c_node_id = data + 32;
348 }
288
349
289 entry = Py_BuildValue("Liiiiiis#", offset_flags, comp_len,
350 comp_len = ntohl(decode[2]);
351 uncomp_len = ntohl(decode[3]);
352 base_rev = ntohl(decode[4]);
353 link_rev = ntohl(decode[5]);
354 parent_1 = ntohl(decode[6]);
355 parent_2 = ntohl(decode[7]);
356 c_node_id = data + 32;
357
358 entry = Py_BuildValue(tuple_format, offset_flags, comp_len,
290 uncomp_len, base_rev, link_rev,
359 uncomp_len, base_rev, link_rev,
291 parent_1, parent_2, c_node_id, 20);
360 parent_1, parent_2, c_node_id, 20);
292
361
293 if (!entry)
362 if (entry)
294 return 0;
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 }
295
384
296 PyObject_GC_UnTrack(entry); /* don't waste time with this */
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;
297
411
298 if (inlined) {
412 Py_RETURN_NONE;
299 err = PyList_Append(index, entry);
413 }
300 Py_DECREF(entry);
414
301 if (err)
415 static int index_assign_subscript(indexObject *self, PyObject *item,
302 return 0;
416 PyObject *value)
303 } else
417 {
304 PyList_SET_ITEM(index, n, entry); /* steals reference */
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)
432 return 0;
433
434 if ((step < 0 && start < stop) || (step > 0 && start > stop))
435 stop = start;
305
436
306 n++;
437 if (step < 0) {
307 step = 64 + (inlined ? comp_len : 0);
438 stop = start + 1;
308 if (data + step > end || data + step < data)
439 start = stop + step*(slicelength - 1) - 1;
309 break;
440 step = -step;
310 data += step;
441 }
442
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) {
448
313 if (!PyErr_Occurred())
449 if (stop != length - 1) {
314 PyErr_SetString(PyExc_ValueError, "corrupt index file");
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 return 0;
461 return 0;
316 }
462 }
317
463
318 /* create the magic nullid entry in the index at [-1] */
464 return PyList_SetSlice(self->added, start - self->length + 1,
319 entry = Py_BuildValue("Liiiiiis#", (uint64_t)0, 0, 0, -1, -1, -1, -1, nullid, 20);
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)
477 while (data + hdrsize <= end) {
322 return 0;
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) {
503 static int index_real_init(indexObject *self, const char *data, int size,
327 err = PyList_Append(index, entry);
504 PyObject *inlined_obj, PyObject *data_obj)
328 Py_DECREF(entry);
505 {
329 if (err)
506 self->inlined = inlined_obj && PyObject_IsTrue(inlined_obj);
330 return 0;
507 self->data = data_obj;
331 } else
508 self->cache = NULL;
332 PyList_SET_ITEM(index, n, entry); /* steals reference */
509
510 self->added = NULL;
511 self->offsets = NULL;
512 Py_INCREF(self->data);
333
513
334 return 1;
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
529 return 0;
530 bail:
531 return -1;
532 }
533
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
547 static void index_dealloc(indexObject *self)
338 * following format: (index, cache)
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
626 * index: an index object that lazily parses the RevlogNG records
341 * cache: if data is inlined, a tuple (index_file_content, 0) else None
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 static PyObject *parse_index2(PyObject *self, PyObject *args)
631 static PyObject *parse_index2(PyObject *self, PyObject *args)
344 {
632 {
345 const char *data;
633 const char *data;
346 int size, inlined;
634 int size, ret;
347 PyObject *rval = NULL, *index = NULL, *cache = NULL;
635 PyObject *inlined_obj, *tuple = NULL, *cache = NULL;
348 PyObject *data_obj = NULL, *inlined_obj;
636 indexObject *idx;
349
637
350 if (!PyArg_ParseTuple(args, "s#O", &data, &size, &inlined_obj))
638 if (!PyArg_ParseTuple(args, "s#O", &data, &size, &inlined_obj))
351 return NULL;
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
646 ret = index_real_init(idx, data, size, inlined_obj,
355 * advance: size divided by the size of one revlog record (64 bytes)
647 PyTuple_GET_ITEM(args, 0));
356 * plus one for nullid */
648 if (ret)
357 index = inlined ? PyList_New(0) : PyList_New(size / 64 + 1);
649 goto bail;
358 if (!index)
359 goto quit;
360
650
361 /* set up the cache return value */
651 if (idx->inlined) {
362 if (inlined) {
652 Py_INCREF(idx->data);
363 /* Note that the reference to data_obj is only borrowed */
653 cache = Py_BuildValue("iO", 0, idx->data);
364 data_obj = PyTuple_GET_ITEM(args, 0);
654 if (cache == NULL)
365 cache = Py_BuildValue("iO", 0, data_obj);
655 goto bail;
366 if (!cache)
367 goto quit;
368 } else {
656 } else {
369 cache = Py_None;
657 cache = Py_None;
370 Py_INCREF(Py_None);
658 Py_INCREF(cache);
371 }
659 }
372
660
373 /* actually populate the index with data */
661 tuple = Py_BuildValue("NN", idx, cache);
374 if (!_parse_index_ng(data, size, inlined, index))
662 if (!tuple)
375 goto quit;
663 goto bail;
664 return tuple;
376
665
377 rval = Py_BuildValue("NN", index, cache);
666 bail:
378 if (!rval)
667 Py_XDECREF(idx);
379 goto quit;
380 return rval;
381
382 quit:
383 Py_XDECREF(index);
384 Py_XDECREF(cache);
668 Py_XDECREF(cache);
385 Py_XDECREF(rval);
669 Py_XDECREF(tuple);
386 return NULL;
670 return NULL;
387 }
671 }
388
672
389
390 static char parsers_doc[] = "Efficient content parsing.";
673 static char parsers_doc[] = "Efficient content parsing.";
391
674
392 static PyMethodDef methods[] = {
675 static PyMethodDef methods[] = {
393 {"parse_manifest", parse_manifest, METH_VARARGS, "parse a manifest\n"},
676 {"parse_manifest", parse_manifest, METH_VARARGS, "parse a manifest\n"},
394 {"parse_dirstate", parse_dirstate, METH_VARARGS, "parse a dirstate\n"},
677 {"parse_dirstate", parse_dirstate, METH_VARARGS, "parse a dirstate\n"},
395 {"parse_index2", parse_index2, METH_VARARGS, "parse a revlog index\n"},
678 {"parse_index2", parse_index2, METH_VARARGS, "parse a revlog index\n"},
396 {NULL, NULL}
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 #ifdef IS_PY3K
698 #ifdef IS_PY3K
400 static struct PyModuleDef parsers_module = {
699 static struct PyModuleDef parsers_module = {
401 PyModuleDef_HEAD_INIT,
700 PyModuleDef_HEAD_INIT,
402 "parsers",
701 "parsers",
403 parsers_doc,
702 parsers_doc,
404 -1,
703 -1,
405 methods
704 methods
406 };
705 };
407
706
408 PyMODINIT_FUNC PyInit_parsers(void)
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 #else
713 #else
413 PyMODINIT_FUNC initparsers(void)
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 #endif
719 #endif
418
720
@@ -1,113 +1,115 b''
1 from mercurial import parsers
1 from mercurial import parsers
2 from mercurial.node import nullid, nullrev
2 from mercurial.node import nullid, nullrev
3 import struct
3 import struct
4
4
5 # This unit test compares the return value of the original Python
5 # This unit test compares the return value of the original Python
6 # implementation of parseindex and the new C implementation for
6 # implementation of parseindex and the new C implementation for
7 # an index file with and without inlined data
7 # an index file with and without inlined data
8
8
9 # original python implementation
9 # original python implementation
10 def gettype(q):
10 def gettype(q):
11 return int(q & 0xFFFF)
11 return int(q & 0xFFFF)
12
12
13 def offset_type(offset, type):
13 def offset_type(offset, type):
14 return long(long(offset) << 16 | type)
14 return long(long(offset) << 16 | type)
15
15
16 indexformatng = ">Qiiiiii20s12x"
16 indexformatng = ">Qiiiiii20s12x"
17
17
18 def py_parseindex(data, inline) :
18 def py_parseindex(data, inline) :
19 s = 64
19 s = 64
20 cache = None
20 cache = None
21 index = []
21 index = []
22 nodemap = {nullid: nullrev}
22 nodemap = {nullid: nullrev}
23 n = off = 0
23 n = off = 0
24
24
25 l = len(data) - s
25 l = len(data) - s
26 append = index.append
26 append = index.append
27 if inline:
27 if inline:
28 cache = (0, data)
28 cache = (0, data)
29 while off <= l:
29 while off <= l:
30 e = struct.unpack(indexformatng, data[off:off + s])
30 e = struct.unpack(indexformatng, data[off:off + s])
31 nodemap[e[7]] = n
31 nodemap[e[7]] = n
32 append(e)
32 append(e)
33 n += 1
33 n += 1
34 if e[1] < 0:
34 if e[1] < 0:
35 break
35 break
36 off += e[1] + s
36 off += e[1] + s
37 else:
37 else:
38 while off <= l:
38 while off <= l:
39 e = struct.unpack(indexformatng, data[off:off + s])
39 e = struct.unpack(indexformatng, data[off:off + s])
40 nodemap[e[7]] = n
40 nodemap[e[7]] = n
41 append(e)
41 append(e)
42 n += 1
42 n += 1
43 off += s
43 off += s
44
44
45 e = list(index[0])
45 e = list(index[0])
46 type = gettype(e[0])
46 type = gettype(e[0])
47 e[0] = offset_type(0, type)
47 e[0] = offset_type(0, type)
48 index[0] = tuple(e)
48 index[0] = tuple(e)
49
49
50 # add the magic null revision at -1
50 # add the magic null revision at -1
51 index.append((0, 0, 0, -1, -1, -1, -1, nullid))
51 index.append((0, 0, 0, -1, -1, -1, -1, nullid))
52
52
53 return index, cache
53 return index, cache
54
54
55
56 data_inlined = '\x00\x01\x00\x01\x00\x00\x00\x00\x00\x00\x01\x8c' \
55 data_inlined = '\x00\x01\x00\x01\x00\x00\x00\x00\x00\x00\x01\x8c' \
57 '\x00\x00\x04\x07\x00\x00\x00\x00\x00\x00\x15\x15\xff\xff\xff' \
56 '\x00\x00\x04\x07\x00\x00\x00\x00\x00\x00\x15\x15\xff\xff\xff' \
58 '\xff\xff\xff\xff\xff\xebG\x97\xb7\x1fB\x04\xcf\x13V\x81\tw\x1b' \
57 '\xff\xff\xff\xff\xff\xebG\x97\xb7\x1fB\x04\xcf\x13V\x81\tw\x1b' \
59 'w\xdduR\xda\xc6\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' \
58 'w\xdduR\xda\xc6\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' \
60 'x\x9c\x9d\x93?O\xc30\x10\xc5\xf7|\x8a\xdb\x9a\xa8m\x06\xd8*\x95' \
59 'x\x9c\x9d\x93?O\xc30\x10\xc5\xf7|\x8a\xdb\x9a\xa8m\x06\xd8*\x95' \
61 '\x81B\xa1\xa2\xa2R\xcb\x86Pd\x9a\x0b5$vd_\x04\xfd\xf6\x9c\xff@' \
60 '\x81B\xa1\xa2\xa2R\xcb\x86Pd\x9a\x0b5$vd_\x04\xfd\xf6\x9c\xff@' \
62 '\x11!\x0b\xd9\xec\xf7\xbbw\xe7gG6\xad6\x04\xdaN\xc0\x92\xa0$)' \
61 '\x11!\x0b\xd9\xec\xf7\xbbw\xe7gG6\xad6\x04\xdaN\xc0\x92\xa0$)' \
63 '\xb1\x82\xa2\xd1%\x16\xa4\x8b7\xa9\xca\xd4-\xb2Y\x02\xfc\xc9' \
62 '\xb1\x82\xa2\xd1%\x16\xa4\x8b7\xa9\xca\xd4-\xb2Y\x02\xfc\xc9' \
64 '\xcaS\xf9\xaeX\xed\xb6\xd77Q\x02\x83\xd4\x19\xf5--Y\xea\xe1W' \
63 '\xcaS\xf9\xaeX\xed\xb6\xd77Q\x02\x83\xd4\x19\xf5--Y\xea\xe1W' \
65 '\xab\xed\x10\xceR\x0f_\xdf\xdf\r\xe1,\xf5\xf0\xcb\xf5 \xceR\x0f' \
64 '\xab\xed\x10\xceR\x0f_\xdf\xdf\r\xe1,\xf5\xf0\xcb\xf5 \xceR\x0f' \
66 '_\xdc\x0e\x0e\xc3R\x0f_\xae\x96\x9b!\x9e\xa5\x1e\xbf\xdb,\x06' \
65 '_\xdc\x0e\x0e\xc3R\x0f_\xae\x96\x9b!\x9e\xa5\x1e\xbf\xdb,\x06' \
67 '\xc7q\x9a/\x88\x82\xc3B\xea\xb5\xb4TJ\x93\xb6\x82\x0e\xe16\xe6' \
66 '\xc7q\x9a/\x88\x82\xc3B\xea\xb5\xb4TJ\x93\xb6\x82\x0e\xe16\xe6' \
68 'KQ\xdb\xaf\xecG\xa3\xd1 \x01\xd3\x0b_^\xe8\xaa\xa0\xae\xad\xd1' \
67 'KQ\xdb\xaf\xecG\xa3\xd1 \x01\xd3\x0b_^\xe8\xaa\xa0\xae\xad\xd1' \
69 '&\xbef\x1bz\x08\xb0|\xc9Xz\x06\xf6Z\x91\x90J\xaa\x17\x90\xaa' \
68 '&\xbef\x1bz\x08\xb0|\xc9Xz\x06\xf6Z\x91\x90J\xaa\x17\x90\xaa' \
70 '\xd2\xa6\x11$5C\xcf\xba#\xa0\x03\x02*2\x92-\xfc\xb1\x94\xdf\xe2' \
69 '\xd2\xa6\x11$5C\xcf\xba#\xa0\x03\x02*2\x92-\xfc\xb1\x94\xdf\xe2' \
71 '\xae\xb8\'m\x8ey0^\x85\xd3\x82\xb4\xf0`:\x9c\x00\x8a\xfd\x01' \
70 '\xae\xb8\'m\x8ey0^\x85\xd3\x82\xb4\xf0`:\x9c\x00\x8a\xfd\x01' \
72 '\xb0\xc6\x86\x8b\xdd\xae\x80\xf3\xa9\x9fd\x16\n\x00R%\x1a\x06' \
71 '\xb0\xc6\x86\x8b\xdd\xae\x80\xf3\xa9\x9fd\x16\n\x00R%\x1a\x06' \
73 '\xe9\xd8b\x98\x1d\xf4\xf3+\x9bf\x01\xd8p\x1b\xf3.\xed\x9f^g\xc3' \
72 '\xe9\xd8b\x98\x1d\xf4\xf3+\x9bf\x01\xd8p\x1b\xf3.\xed\x9f^g\xc3' \
74 '^\xd9W81T\xdb\xd5\x04sx|\xf2\xeb\xd6`%?x\xed"\x831\xbf\xf3\xdc' \
73 '^\xd9W81T\xdb\xd5\x04sx|\xf2\xeb\xd6`%?x\xed"\x831\xbf\xf3\xdc' \
75 'b\xeb%gaY\xe1\xad\x9f\xb9f\'1w\xa9\xa5a\x83s\x82J\xb98\xbc4\x8b' \
74 'b\xeb%gaY\xe1\xad\x9f\xb9f\'1w\xa9\xa5a\x83s\x82J\xb98\xbc4\x8b' \
76 '\x83\x00\x9f$z\xb8#\xa5\xb1\xdf\x98\xd9\xec\x1b\x89O\xe3Ts\x9a4' \
75 '\x83\x00\x9f$z\xb8#\xa5\xb1\xdf\x98\xd9\xec\x1b\x89O\xe3Ts\x9a4' \
77 '\x17m\x8b\xfc\x8f\xa5\x95\x9a\xfc\xfa\xed,\xe5|\xa1\xfe\x15\xb9' \
76 '\x17m\x8b\xfc\x8f\xa5\x95\x9a\xfc\xfa\xed,\xe5|\xa1\xfe\x15\xb9' \
78 '\xbc\xb2\x93\x1f\xf2\x95\xff\xdf,\x1a\xc5\xe7\x17*\x93Oz:>\x0e'
77 '\xbc\xb2\x93\x1f\xf2\x95\xff\xdf,\x1a\xc5\xe7\x17*\x93Oz:>\x0e'
79
78
80 data_non_inlined = '\x00\x00\x00\x01\x00\x00\x00\x00\x00\x01D\x19' \
79 data_non_inlined = '\x00\x00\x00\x01\x00\x00\x00\x00\x00\x01D\x19' \
81 '\x00\x07e\x12\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff' \
80 '\x00\x07e\x12\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff' \
82 '\xff\xff\xff\xff\xd1\xf4\xbb\xb0\xbe\xfc\x13\xbd\x8c\xd3\x9d' \
81 '\xff\xff\xff\xff\xd1\xf4\xbb\xb0\xbe\xfc\x13\xbd\x8c\xd3\x9d' \
83 '\x0f\xcd\xd9;\x8c\x07\x8cJ/\x00\x00\x00\x00\x00\x00\x00\x00\x00' \
82 '\x0f\xcd\xd9;\x8c\x07\x8cJ/\x00\x00\x00\x00\x00\x00\x00\x00\x00' \
84 '\x00\x00\x00\x00\x00\x00\x01D\x19\x00\x00\x00\x00\x00\xdf\x00' \
83 '\x00\x00\x00\x00\x00\x00\x01D\x19\x00\x00\x00\x00\x00\xdf\x00' \
85 '\x00\x01q\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\xff' \
84 '\x00\x01q\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\xff' \
86 '\xff\xff\xff\xc1\x12\xb9\x04\x96\xa4Z1t\x91\xdfsJ\x90\xf0\x9bh' \
85 '\xff\xff\xff\xc1\x12\xb9\x04\x96\xa4Z1t\x91\xdfsJ\x90\xf0\x9bh' \
87 '\x07l&\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' \
86 '\x07l&\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' \
88 '\x00\x01D\xf8\x00\x00\x00\x00\x01\x1b\x00\x00\x01\xb8\x00\x00' \
87 '\x00\x01D\xf8\x00\x00\x00\x00\x01\x1b\x00\x00\x01\xb8\x00\x00' \
89 '\x00\x01\x00\x00\x00\x02\x00\x00\x00\x01\xff\xff\xff\xff\x02\n' \
88 '\x00\x01\x00\x00\x00\x02\x00\x00\x00\x01\xff\xff\xff\xff\x02\n' \
90 '\x0e\xc6&\xa1\x92\xae6\x0b\x02i\xfe-\xe5\xbao\x05\xd1\xe7\x00' \
89 '\x0e\xc6&\xa1\x92\xae6\x0b\x02i\xfe-\xe5\xbao\x05\xd1\xe7\x00' \
91 '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01F' \
90 '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01F' \
92 '\x13\x00\x00\x00\x00\x01\xec\x00\x00\x03\x06\x00\x00\x00\x01' \
91 '\x13\x00\x00\x00\x00\x01\xec\x00\x00\x03\x06\x00\x00\x00\x01' \
93 '\x00\x00\x00\x03\x00\x00\x00\x02\xff\xff\xff\xff\x12\xcb\xeby1' \
92 '\x00\x00\x00\x03\x00\x00\x00\x02\xff\xff\xff\xff\x12\xcb\xeby1' \
94 '\xb6\r\x98B\xcb\x07\xbd`\x8f\x92\xd9\xc4\x84\xbdK\x00\x00\x00' \
93 '\xb6\r\x98B\xcb\x07\xbd`\x8f\x92\xd9\xc4\x84\xbdK\x00\x00\x00' \
95 '\x00\x00\x00\x00\x00\x00\x00\x00\x00'
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 py_res_1 = py_parseindex(data_inlined, True)
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 py_res_2 = py_parseindex(data_non_inlined, False)
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 if py_res_1 != c_res_1:
107 if py_res_1 != c_res_1:
106 print "Parse index result (with inlined data) differs!"
108 print "Parse index result (with inlined data) differs!"
107
109
108 if py_res_2 != c_res_2:
110 if py_res_2 != c_res_2:
109 print "Parse index result (no inlined data) differs!"
111 print "Parse index result (no inlined data) differs!"
110
112
111 print "done"
113 print "done"
112
114
113 runtest()
115 runtest()
General Comments 0
You need to be logged in to leave comments. Login now