##// END OF EJS Templates
C implementation of revlog index parsing
Bernhard Leiner -
r7108:1ca878d7 default
parent child Browse files
Show More
@@ -1,248 +1,415 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 static int hexdigit(char c)
14 static int hexdigit(char c)
15 {
15 {
16 if (c >= '0' && c <= '9')
16 if (c >= '0' && c <= '9')
17 return c - '0';
17 return c - '0';
18 if (c >= 'a' && c <= 'f')
18 if (c >= 'a' && c <= 'f')
19 return c - 'a' + 10;
19 return c - 'a' + 10;
20 if (c >= 'A' && c <= 'F')
20 if (c >= 'A' && c <= 'F')
21 return c - 'A' + 10;
21 return c - 'A' + 10;
22
22
23 PyErr_SetString(PyExc_ValueError, "input contains non-hex character");
23 PyErr_SetString(PyExc_ValueError, "input contains non-hex character");
24 return 0;
24 return 0;
25 }
25 }
26
26
27 /*
27 /*
28 * Turn a hex-encoded string into binary.
28 * Turn a hex-encoded string into binary.
29 */
29 */
30 static PyObject *unhexlify(const char *str, int len)
30 static PyObject *unhexlify(const char *str, int len)
31 {
31 {
32 PyObject *ret;
32 PyObject *ret;
33 const char *c;
33 const char *c;
34 char *d;
34 char *d;
35
35
36 ret = PyString_FromStringAndSize(NULL, len / 2);
36 ret = PyString_FromStringAndSize(NULL, len / 2);
37 if (!ret)
37 if (!ret)
38 return NULL;
38 return NULL;
39
39
40 d = PyString_AS_STRING(ret);
40 d = PyString_AS_STRING(ret);
41 for (c = str; c < str + len;) {
41 for (c = str; c < str + len;) {
42 int hi = hexdigit(*c++);
42 int hi = hexdigit(*c++);
43 int lo = hexdigit(*c++);
43 int lo = hexdigit(*c++);
44 *d++ = (hi << 4) | lo;
44 *d++ = (hi << 4) | lo;
45 }
45 }
46
46
47 return ret;
47 return ret;
48 }
48 }
49
49
50 /*
50 /*
51 * This code assumes that a manifest is stitched together with newline
51 * This code assumes that a manifest is stitched together with newline
52 * ('\n') characters.
52 * ('\n') characters.
53 */
53 */
54 static PyObject *parse_manifest(PyObject *self, PyObject *args)
54 static PyObject *parse_manifest(PyObject *self, PyObject *args)
55 {
55 {
56 PyObject *mfdict, *fdict;
56 PyObject *mfdict, *fdict;
57 char *str, *cur, *start, *zero;
57 char *str, *cur, *start, *zero;
58 int len;
58 int len;
59
59
60 if (!PyArg_ParseTuple(args, "O!O!s#:parse_manifest",
60 if (!PyArg_ParseTuple(args, "O!O!s#:parse_manifest",
61 &PyDict_Type, &mfdict,
61 &PyDict_Type, &mfdict,
62 &PyDict_Type, &fdict,
62 &PyDict_Type, &fdict,
63 &str, &len))
63 &str, &len))
64 goto quit;
64 goto quit;
65
65
66 for (start = cur = str, zero = NULL; cur < str + len; cur++) {
66 for (start = cur = str, zero = NULL; cur < str + len; cur++) {
67 PyObject *file = NULL, *node = NULL;
67 PyObject *file = NULL, *node = NULL;
68 PyObject *flags = NULL;
68 PyObject *flags = NULL;
69 int nlen;
69 int nlen;
70
70
71 if (!*cur) {
71 if (!*cur) {
72 zero = cur;
72 zero = cur;
73 continue;
73 continue;
74 }
74 }
75 else if (*cur != '\n')
75 else if (*cur != '\n')
76 continue;
76 continue;
77
77
78 if (!zero) {
78 if (!zero) {
79 PyErr_SetString(PyExc_ValueError,
79 PyErr_SetString(PyExc_ValueError,
80 "manifest entry has no separator");
80 "manifest entry has no separator");
81 goto quit;
81 goto quit;
82 }
82 }
83
83
84 file = PyString_FromStringAndSize(start, zero - start);
84 file = PyString_FromStringAndSize(start, zero - start);
85 if (!file)
85 if (!file)
86 goto bail;
86 goto bail;
87
87
88 nlen = cur - zero - 1;
88 nlen = cur - zero - 1;
89
89
90 node = unhexlify(zero + 1, nlen > 40 ? 40 : nlen);
90 node = unhexlify(zero + 1, nlen > 40 ? 40 : nlen);
91 if (!node)
91 if (!node)
92 goto bail;
92 goto bail;
93
93
94 if (nlen > 40) {
94 if (nlen > 40) {
95 PyObject *flags;
95 PyObject *flags;
96
96
97 flags = PyString_FromStringAndSize(zero + 41,
97 flags = PyString_FromStringAndSize(zero + 41,
98 nlen - 40);
98 nlen - 40);
99 if (!flags)
99 if (!flags)
100 goto bail;
100 goto bail;
101
101
102 if (PyDict_SetItem(fdict, file, flags) == -1)
102 if (PyDict_SetItem(fdict, file, flags) == -1)
103 goto bail;
103 goto bail;
104 }
104 }
105
105
106 if (PyDict_SetItem(mfdict, file, node) == -1)
106 if (PyDict_SetItem(mfdict, file, node) == -1)
107 goto bail;
107 goto bail;
108
108
109 start = cur + 1;
109 start = cur + 1;
110 zero = NULL;
110 zero = NULL;
111
111
112 Py_XDECREF(flags);
112 Py_XDECREF(flags);
113 Py_XDECREF(node);
113 Py_XDECREF(node);
114 Py_XDECREF(file);
114 Py_XDECREF(file);
115 continue;
115 continue;
116 bail:
116 bail:
117 Py_XDECREF(flags);
117 Py_XDECREF(flags);
118 Py_XDECREF(node);
118 Py_XDECREF(node);
119 Py_XDECREF(file);
119 Py_XDECREF(file);
120 goto quit;
120 goto quit;
121 }
121 }
122
122
123 if (len > 0 && *(cur - 1) != '\n') {
123 if (len > 0 && *(cur - 1) != '\n') {
124 PyErr_SetString(PyExc_ValueError,
124 PyErr_SetString(PyExc_ValueError,
125 "manifest contains trailing garbage");
125 "manifest contains trailing garbage");
126 goto quit;
126 goto quit;
127 }
127 }
128
128
129 Py_INCREF(Py_None);
129 Py_INCREF(Py_None);
130 return Py_None;
130 return Py_None;
131 quit:
131 quit:
132 return NULL;
132 return NULL;
133 }
133 }
134
134
135 #ifdef _WIN32
135 #ifdef _WIN32
136 # ifdef _MSC_VER
136 # ifdef _MSC_VER
137 /* msvc 6.0 has problems */
137 /* msvc 6.0 has problems */
138 # define inline __inline
138 # define inline __inline
139 typedef unsigned long uint32_t;
139 typedef unsigned long uint32_t;
140 # else
140 # else
141 # include <stdint.h>
141 # include <stdint.h>
142 # endif
142 # endif
143 static uint32_t ntohl(uint32_t x)
143 static uint32_t ntohl(uint32_t x)
144 {
144 {
145 return ((x & 0x000000ffUL) << 24) |
145 return ((x & 0x000000ffUL) << 24) |
146 ((x & 0x0000ff00UL) << 8) |
146 ((x & 0x0000ff00UL) << 8) |
147 ((x & 0x00ff0000UL) >> 8) |
147 ((x & 0x00ff0000UL) >> 8) |
148 ((x & 0xff000000UL) >> 24);
148 ((x & 0xff000000UL) >> 24);
149 }
149 }
150 #else
150 #else
151 /* not windows */
151 /* not windows */
152 # include <sys/types.h>
152 # include <sys/types.h>
153 # if defined __BEOS__ && !defined __HAIKU__
153 # if defined __BEOS__ && !defined __HAIKU__
154 # include <ByteOrder.h>
154 # include <ByteOrder.h>
155 # else
155 # else
156 # include <arpa/inet.h>
156 # include <arpa/inet.h>
157 # endif
157 # endif
158 # include <inttypes.h>
158 # include <inttypes.h>
159 #endif
159 #endif
160
160
161 static PyObject *parse_dirstate(PyObject *self, PyObject *args)
161 static PyObject *parse_dirstate(PyObject *self, PyObject *args)
162 {
162 {
163 PyObject *dmap, *cmap, *parents = NULL, *ret = NULL;
163 PyObject *dmap, *cmap, *parents = NULL, *ret = NULL;
164 PyObject *fname = NULL, *cname = NULL, *entry = NULL;
164 PyObject *fname = NULL, *cname = NULL, *entry = NULL;
165 char *str, *cur, *end, *cpos;
165 char *str, *cur, *end, *cpos;
166 int state, mode, size, mtime, flen;
166 int state, mode, size, mtime, flen;
167 int len;
167 int len;
168 char decode[16]; /* for alignment */
168 char decode[16]; /* for alignment */
169
169
170 if (!PyArg_ParseTuple(args, "O!O!s#:parse_dirstate",
170 if (!PyArg_ParseTuple(args, "O!O!s#:parse_dirstate",
171 &PyDict_Type, &dmap,
171 &PyDict_Type, &dmap,
172 &PyDict_Type, &cmap,
172 &PyDict_Type, &cmap,
173 &str, &len))
173 &str, &len))
174 goto quit;
174 goto quit;
175
175
176 /* read parents */
176 /* read parents */
177 if (len < 40)
177 if (len < 40)
178 goto quit;
178 goto quit;
179
179
180 parents = Py_BuildValue("s#s#", str, 20, str + 20, 20);
180 parents = Py_BuildValue("s#s#", str, 20, str + 20, 20);
181 if (!parents)
181 if (!parents)
182 goto quit;
182 goto quit;
183
183
184 /* read filenames */
184 /* read filenames */
185 cur = str + 40;
185 cur = str + 40;
186 end = str + len;
186 end = str + len;
187
187
188 while (cur < end - 17) {
188 while (cur < end - 17) {
189 /* unpack header */
189 /* unpack header */
190 state = *cur;
190 state = *cur;
191 memcpy(decode, cur + 1, 16);
191 memcpy(decode, cur + 1, 16);
192 mode = ntohl(*(uint32_t *)(decode));
192 mode = ntohl(*(uint32_t *)(decode));
193 size = ntohl(*(uint32_t *)(decode + 4));
193 size = ntohl(*(uint32_t *)(decode + 4));
194 mtime = ntohl(*(uint32_t *)(decode + 8));
194 mtime = ntohl(*(uint32_t *)(decode + 8));
195 flen = ntohl(*(uint32_t *)(decode + 12));
195 flen = ntohl(*(uint32_t *)(decode + 12));
196 cur += 17;
196 cur += 17;
197 if (cur + flen > end)
197 if (cur + flen > end)
198 goto quit;
198 goto quit;
199
199
200 entry = Py_BuildValue("ciii", state, mode, size, mtime);
200 entry = Py_BuildValue("ciii", state, mode, size, mtime);
201 PyObject_GC_UnTrack(entry); /* don't waste time with this */
201 PyObject_GC_UnTrack(entry); /* don't waste time with this */
202 if (!entry)
202 if (!entry)
203 goto quit;
203 goto quit;
204
204
205 cpos = memchr(cur, 0, flen);
205 cpos = memchr(cur, 0, flen);
206 if (cpos) {
206 if (cpos) {
207 fname = PyString_FromStringAndSize(cur, cpos - cur);
207 fname = PyString_FromStringAndSize(cur, cpos - cur);
208 cname = PyString_FromStringAndSize(cpos + 1,
208 cname = PyString_FromStringAndSize(cpos + 1,
209 flen - (cpos - cur) - 1);
209 flen - (cpos - cur) - 1);
210 if (!fname || !cname ||
210 if (!fname || !cname ||
211 PyDict_SetItem(cmap, fname, cname) == -1 ||
211 PyDict_SetItem(cmap, fname, cname) == -1 ||
212 PyDict_SetItem(dmap, fname, entry) == -1)
212 PyDict_SetItem(dmap, fname, entry) == -1)
213 goto quit;
213 goto quit;
214 Py_DECREF(cname);
214 Py_DECREF(cname);
215 } else {
215 } else {
216 fname = PyString_FromStringAndSize(cur, flen);
216 fname = PyString_FromStringAndSize(cur, flen);
217 if (!fname ||
217 if (!fname ||
218 PyDict_SetItem(dmap, fname, entry) == -1)
218 PyDict_SetItem(dmap, fname, entry) == -1)
219 goto quit;
219 goto quit;
220 }
220 }
221 cur += flen;
221 cur += flen;
222 Py_DECREF(fname);
222 Py_DECREF(fname);
223 Py_DECREF(entry);
223 Py_DECREF(entry);
224 fname = cname = entry = NULL;
224 fname = cname = entry = NULL;
225 }
225 }
226
226
227 ret = parents;
227 ret = parents;
228 Py_INCREF(ret);
228 Py_INCREF(ret);
229 quit:
229 quit:
230 Py_XDECREF(fname);
230 Py_XDECREF(fname);
231 Py_XDECREF(cname);
231 Py_XDECREF(cname);
232 Py_XDECREF(entry);
232 Py_XDECREF(entry);
233 Py_XDECREF(parents);
233 Py_XDECREF(parents);
234 return ret;
234 return ret;
235 }
235 }
236
236
237
238 static inline uint64_t ntohll(uint64_t x)
239 {
240 return (((uint64_t)ntohl((uint32_t)x)) << 32) |
241 (uint64_t)ntohl((uint32_t)(x >> 32));
242 }
243
244 const char nullid[20];
245 const int nullrev = -1;
246
247 /* RevlogNG format (all in big endian, data may be inlined):
248 * 6 bytes: offset
249 * 2 bytes: flags
250 * 4 bytes: compressed length
251 * 4 bytes: uncompressed length
252 * 4 bytes: base revision
253 * 4 bytes: link revision
254 * 4 bytes: parent 1 revision
255 * 4 bytes: parent 2 revision
256 * 32 bytes: nodeid (only 20 bytes used)
257 */
258 static int _parse_index_ng (const char *data, int size, int inlined,
259 PyObject *index, PyObject *nodemap)
260 {
261 PyObject *entry = NULL, *node_id = NULL, *n_obj = NULL;
262 PyObject *nullrev_obj = NULL, *nullid_obj = NULL;
263 int comp_len, uncomp_len, base_rev, link_rev, parent_1, parent_2;
264 uint64_t offset_flags;
265 int n = 0;
266 const char *end = data + size;
267
268 while (data < end) {
269 offset_flags = ntohll(*((uint64_t *) data));
270 if (n == 0) /* mask out version number for the first entry */
271 offset_flags &= 0xFFFF;
272
273 comp_len = ntohl(*((uint32_t *) (data + 8)));
274 uncomp_len = ntohl(*((uint32_t *) (data + 12)));
275 base_rev = ntohl(*((uint32_t *) (data + 16)));
276 link_rev = ntohl(*((uint32_t *) (data + 20)));
277 parent_1 = ntohl(*((uint32_t *) (data + 24)));
278 parent_2 = ntohl(*((uint32_t *) (data + 28)));
279 node_id = PyString_FromStringAndSize(data + 32, 20);
280 n_obj = PyInt_FromLong(n);
281 if (!node_id || !n_obj ||
282 PyDict_SetItem(nodemap, node_id, n_obj) != 0)
283 goto quit;
284 Py_DECREF(n_obj);
285
286 entry = Py_BuildValue("LiiiiiiN", offset_flags, comp_len,
287 uncomp_len, base_rev, link_rev,
288 parent_1, parent_2, node_id);
289 PyObject_GC_UnTrack(entry); /* don't waste time with this */
290 if (!entry)
291 goto quit;
292
293 /* append to or set value in the index list */
294 if (inlined) {
295 if (PyList_Append(index, entry) != 0)
296 goto quit;
297 Py_DECREF(entry);
298 } else {
299 PyList_SET_ITEM(index, n, entry); /* steals reference */
300 }
301
302 data += 64 + (inlined ? comp_len : 0);
303 n++;
304 }
305 if (data > end) {
306 if (!PyErr_Occurred())
307 PyErr_SetString(PyExc_ValueError, "corrupt index file");
308 goto quit;
309 }
310
311 /* create the nullid/nullrev entry in the nodemap and the
312 * magic nullid entry in the index at [-1] */
313 nullid_obj = PyString_FromStringAndSize(nullid, 20);
314 nullrev_obj = PyInt_FromLong(nullrev);
315 if (!nodemap || !nullid_obj || !nullrev_obj ||
316 PyDict_SetItem(nodemap, nullid_obj, nullrev_obj) != 0)
317 goto quit;
318 Py_DECREF(nullrev_obj);
319
320 entry = Py_BuildValue("iiiiiiiN", 0, 0, 0, -1, -1, -1, -1, nullid_obj);
321 PyObject_GC_UnTrack(entry); /* don't waste time with this */
322 if (!entry)
323 goto quit;
324 if (inlined) {
325 if (PyList_Append(index, entry) != 0)
326 goto quit;
327 Py_DECREF(entry);
328 } else {
329 PyList_SET_ITEM(index, n, entry); /* steals reference */
330 }
331
332 return 1;
333
334 quit:
335 Py_XDECREF(n_obj);
336 Py_XDECREF(node_id);
337 Py_XDECREF(entry);
338 Py_XDECREF(nullrev_obj);
339 Py_XDECREF(nullid_obj);
340 return 0;
341 }
342
343
344
345 /* This function parses a index file and returns a Python tuple of the
346 * following format: (index, nodemap, cache)
347 *
348 * index: a list of tuples containing the RevlogNG records
349 * nodemap: a dict mapping node ids to indices in the index list
350 * cache: if data is inlined, a tuple (index_file_content, 0) else None
351 */
352 static PyObject *parse_index(PyObject *self, PyObject *args)
353 {
354 const char *data;
355 int size, inlined;
356 PyObject *rval = NULL, *index = NULL, *nodemap = NULL, *cache = NULL;
357 PyObject *data_obj = NULL, *inlined_obj;
358
359 if (!PyArg_ParseTuple(args, "s#O", &data, &size, &inlined_obj))
360 return NULL;
361 inlined = inlined_obj && PyObject_IsTrue(inlined_obj);
362
363 /* If no data is inlined, we know the size of the index list in
364 * advance: size divided by size of one one revlog record (64 bytes)
365 * plus one for the nullid */
366 index = inlined ? PyList_New(0) : PyList_New(size / 64 + 1);
367 if (!index)
368 goto quit;
369
370 nodemap = PyDict_New();
371
372 /* set up the cache return value */
373 if (inlined) {
374 /* Note that the reference to data_obj is only borrowed */
375 data_obj = PyTuple_GET_ITEM(args, 0);
376 cache = Py_BuildValue("iO", 0, data_obj);
377 if (!cache)
378 goto quit;
379 } else {
380 cache = Py_None;
381 Py_INCREF(Py_None);
382 }
383
384 /* actually populate the index and the nodemap with data */
385 if (!_parse_index_ng (data, size, inlined, index, nodemap))
386 goto quit;
387
388 rval = Py_BuildValue("NNN", index, nodemap, cache);
389 if (!rval)
390 goto quit;
391 return rval;
392
393 quit:
394 Py_XDECREF(index);
395 Py_XDECREF(nodemap);
396 Py_XDECREF(cache);
397 Py_XDECREF(rval);
398 Py_XDECREF(data_obj);
399 return NULL;
400 }
401
402
237 static char parsers_doc[] = "Efficient content parsing.";
403 static char parsers_doc[] = "Efficient content parsing.";
238
404
239 static PyMethodDef methods[] = {
405 static PyMethodDef methods[] = {
240 {"parse_manifest", parse_manifest, METH_VARARGS, "parse a manifest\n"},
406 {"parse_manifest", parse_manifest, METH_VARARGS, "parse a manifest\n"},
241 {"parse_dirstate", parse_dirstate, METH_VARARGS, "parse a dirstate\n"},
407 {"parse_dirstate", parse_dirstate, METH_VARARGS, "parse a dirstate\n"},
408 {"parse_index", parse_index, METH_VARARGS, "parse a revlog index\n"},
242 {NULL, NULL}
409 {NULL, NULL}
243 };
410 };
244
411
245 PyMODINIT_FUNC initparsers(void)
412 PyMODINIT_FUNC initparsers(void)
246 {
413 {
247 Py_InitModule3("parsers", methods, parsers_doc);
414 Py_InitModule3("parsers", methods, parsers_doc);
248 }
415 }
General Comments 0
You need to be logged in to leave comments. Login now