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