##// END OF EJS Templates
manifest: include Python.h before standard headers...
Drew Gottlieb -
r24352:d6dbe4d1 default
parent child Browse files
Show More
@@ -1,926 +1,926 b''
1 1 /*
2 2 * manifest.c - manifest type that does on-demand parsing.
3 3 *
4 4 * Copyright 2015, Google Inc.
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 #include <Python.h>
10
9 11 #include <assert.h>
10 12 #include <string.h>
11 13 #include <stdlib.h>
12 14
13 #include <Python.h>
14
15 15 /* VC9 doesn't include bool and lacks stdbool.h based on my searching */
16 16 #ifdef _MSC_VER
17 17 #define true 1
18 18 #define false 0
19 19 typedef unsigned char bool;
20 20 #else
21 21 #include <stdbool.h>
22 22 #endif
23 23
24 24 #define DEFAULT_LINES 100000
25 25
26 26 typedef struct {
27 27 char *start;
28 28 Py_ssize_t len; /* length of line including terminal newline */
29 29 char hash_suffix;
30 30 bool from_malloc;
31 31 bool deleted;
32 32 } line;
33 33
34 34 typedef struct {
35 35 PyObject_HEAD
36 36 PyObject *pydata;
37 37 line *lines;
38 38 int numlines; /* number of line entries */
39 39 int livelines; /* number of non-deleted lines */
40 40 int maxlines; /* allocated number of lines */
41 41 bool dirty;
42 42 } lazymanifest;
43 43
44 44 #define MANIFEST_OOM -1
45 45 #define MANIFEST_NOT_SORTED -2
46 46 #define MANIFEST_MALFORMED -3
47 47
48 48 /* defined in parsers.c */
49 49 PyObject *unhexlify(const char *str, int len);
50 50
51 51 /* get the length of the path for a line */
52 52 static size_t pathlen(line *l) {
53 53 return strlen(l->start);
54 54 }
55 55
56 56 /* get the node value of a single line */
57 57 static PyObject *nodeof(line *l) {
58 58 char *s = l->start;
59 59 ssize_t llen = pathlen(l);
60 60 PyObject *hash = unhexlify(s + llen + 1, 40);
61 61 if (!hash) {
62 62 return NULL;
63 63 }
64 64 if (l->hash_suffix != '\0') {
65 65 char newhash[21];
66 66 memcpy(newhash, PyString_AsString(hash), 20);
67 67 Py_DECREF(hash);
68 68 newhash[20] = l->hash_suffix;
69 69 hash = PyString_FromStringAndSize(newhash, 21);
70 70 }
71 71 return hash;
72 72 }
73 73
74 74 /* get the node hash and flags of a line as a tuple */
75 75 static PyObject *hashflags(line *l)
76 76 {
77 77 char *s = l->start;
78 78 size_t plen = pathlen(l);
79 79 PyObject *hash = nodeof(l);
80 80
81 81 /* 40 for hash, 1 for null byte, 1 for newline */
82 82 size_t hplen = plen + 42;
83 83 Py_ssize_t flen = l->len - hplen;
84 84 PyObject *flags;
85 85 PyObject *tup;
86 86
87 87 if (!hash)
88 88 return NULL;
89 89 flags = PyString_FromStringAndSize(s + hplen - 1, flen);
90 90 if (!flags) {
91 91 Py_DECREF(hash);
92 92 return NULL;
93 93 }
94 94 tup = PyTuple_Pack(2, hash, flags);
95 95 Py_DECREF(flags);
96 96 Py_DECREF(hash);
97 97 return tup;
98 98 }
99 99
100 100 /* if we're about to run out of space in the line index, add more */
101 101 static bool realloc_if_full(lazymanifest *self)
102 102 {
103 103 if (self->numlines == self->maxlines) {
104 104 self->maxlines *= 2;
105 105 self->lines = realloc(self->lines, self->maxlines * sizeof(line));
106 106 }
107 107 return !!self->lines;
108 108 }
109 109
110 110 /*
111 111 * Find the line boundaries in the manifest that 'data' points to and store
112 112 * information about each line in 'self'.
113 113 */
114 114 static int find_lines(lazymanifest *self, char *data, Py_ssize_t len)
115 115 {
116 116 char *prev = NULL;
117 117 while (len > 0) {
118 118 line *l;
119 119 char *next = memchr(data, '\n', len);
120 120 if (!next) {
121 121 return MANIFEST_MALFORMED;
122 122 }
123 123 next++; /* advance past newline */
124 124 if (!realloc_if_full(self)) {
125 125 return MANIFEST_OOM; /* no memory */
126 126 }
127 127 if (prev && strcmp(prev, data) > -1) {
128 128 /* This data isn't sorted, so we have to abort. */
129 129 return MANIFEST_NOT_SORTED;
130 130 }
131 131 l = self->lines + ((self->numlines)++);
132 132 l->start = data;
133 133 l->len = next - data;
134 134 l->hash_suffix = '\0';
135 135 l->from_malloc = false;
136 136 l->deleted = false;
137 137 len = len - l->len;
138 138 prev = data;
139 139 data = next;
140 140 }
141 141 self->livelines = self->numlines;
142 142 return 0;
143 143 }
144 144
145 145 static int lazymanifest_init(lazymanifest *self, PyObject *args)
146 146 {
147 147 char *data;
148 148 Py_ssize_t len;
149 149 int err, ret;
150 150 PyObject *pydata;
151 151 if (!PyArg_ParseTuple(args, "S", &pydata)) {
152 152 return -1;
153 153 }
154 154 err = PyString_AsStringAndSize(pydata, &data, &len);
155 155
156 156 self->dirty = false;
157 157 if (err == -1)
158 158 return -1;
159 159 self->pydata = pydata;
160 160 Py_INCREF(self->pydata);
161 161 Py_BEGIN_ALLOW_THREADS
162 162 self->lines = malloc(DEFAULT_LINES * sizeof(line));
163 163 self->maxlines = DEFAULT_LINES;
164 164 self->numlines = 0;
165 165 if (!self->lines)
166 166 ret = MANIFEST_OOM;
167 167 else
168 168 ret = find_lines(self, data, len);
169 169 Py_END_ALLOW_THREADS
170 170 switch (ret) {
171 171 case 0:
172 172 break;
173 173 case MANIFEST_OOM:
174 174 PyErr_NoMemory();
175 175 break;
176 176 case MANIFEST_NOT_SORTED:
177 177 PyErr_Format(PyExc_ValueError,
178 178 "Manifest lines not in sorted order.");
179 179 break;
180 180 case MANIFEST_MALFORMED:
181 181 PyErr_Format(PyExc_ValueError,
182 182 "Manifest did not end in a newline.");
183 183 break;
184 184 default:
185 185 PyErr_Format(PyExc_ValueError,
186 186 "Unknown problem parsing manifest.");
187 187 }
188 188 return ret == 0 ? 0 : -1;
189 189 }
190 190
191 191 static void lazymanifest_dealloc(lazymanifest *self)
192 192 {
193 193 /* free any extra lines we had to allocate */
194 194 int i;
195 195 for (i = 0; i < self->numlines; i++) {
196 196 if (self->lines[i].from_malloc) {
197 197 free(self->lines[i].start);
198 198 }
199 199 }
200 200 if (self->lines) {
201 201 free(self->lines);
202 202 self->lines = NULL;
203 203 }
204 204 if (self->pydata) {
205 205 Py_DECREF(self->pydata);
206 206 self->pydata = NULL;
207 207 }
208 208 PyObject_Del(self);
209 209 }
210 210
211 211 /* iteration support */
212 212
213 213 typedef struct {
214 214 PyObject_HEAD lazymanifest *m;
215 215 Py_ssize_t pos;
216 216 } lmIter;
217 217
218 218 static void lmiter_dealloc(PyObject *o)
219 219 {
220 220 lmIter *self = (lmIter *)o;
221 221 Py_DECREF(self->m);
222 222 PyObject_Del(self);
223 223 }
224 224
225 225 static line *lmiter_nextline(lmIter *self)
226 226 {
227 227 do {
228 228 self->pos++;
229 229 if (self->pos >= self->m->numlines) {
230 230 return NULL;
231 231 }
232 232 /* skip over deleted manifest entries */
233 233 } while (self->m->lines[self->pos].deleted);
234 234 return self->m->lines + self->pos;
235 235 }
236 236
237 237 static PyObject *lmiter_iterentriesnext(PyObject *o)
238 238 {
239 239 size_t pl;
240 240 line *l;
241 241 Py_ssize_t consumed;
242 242 PyObject *path = NULL, *hash = NULL, *flags = NULL;
243 243 l = lmiter_nextline((lmIter *)o);
244 244 if (!l) {
245 245 goto bail;
246 246 }
247 247 pl = pathlen(l);
248 248 path = PyString_FromStringAndSize(l->start, pl);
249 249 hash = nodeof(l);
250 250 consumed = pl + 41;
251 251 flags = PyString_FromStringAndSize(l->start + consumed,
252 252 l->len - consumed - 1);
253 253 if (!path || !hash || !flags) {
254 254 goto bail;
255 255 }
256 256 return PyTuple_Pack(3, path, hash, flags);
257 257 bail:
258 258 Py_XDECREF(path);
259 259 Py_XDECREF(hash);
260 260 Py_XDECREF(flags);
261 261 return NULL;
262 262 }
263 263
264 264 static PyTypeObject lazymanifestEntriesIterator = {
265 265 PyObject_HEAD_INIT(NULL)
266 266 0, /*ob_size */
267 267 "parsers.lazymanifest.entriesiterator", /*tp_name */
268 268 sizeof(lmIter), /*tp_basicsize */
269 269 0, /*tp_itemsize */
270 270 lmiter_dealloc, /*tp_dealloc */
271 271 0, /*tp_print */
272 272 0, /*tp_getattr */
273 273 0, /*tp_setattr */
274 274 0, /*tp_compare */
275 275 0, /*tp_repr */
276 276 0, /*tp_as_number */
277 277 0, /*tp_as_sequence */
278 278 0, /*tp_as_mapping */
279 279 0, /*tp_hash */
280 280 0, /*tp_call */
281 281 0, /*tp_str */
282 282 0, /*tp_getattro */
283 283 0, /*tp_setattro */
284 284 0, /*tp_as_buffer */
285 285 /* tp_flags: Py_TPFLAGS_HAVE_ITER tells python to
286 286 use tp_iter and tp_iternext fields. */
287 287 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_ITER,
288 288 "Iterator for 3-tuples in a lazymanifest.", /* tp_doc */
289 289 0, /* tp_traverse */
290 290 0, /* tp_clear */
291 291 0, /* tp_richcompare */
292 292 0, /* tp_weaklistoffset */
293 293 PyObject_SelfIter, /* tp_iter: __iter__() method */
294 294 lmiter_iterentriesnext, /* tp_iternext: next() method */
295 295 };
296 296
297 297 static PyObject *lmiter_iterkeysnext(PyObject *o)
298 298 {
299 299 size_t pl;
300 300 line *l = lmiter_nextline((lmIter *)o);
301 301 if (!l) {
302 302 return NULL;
303 303 }
304 304 pl = pathlen(l);
305 305 return PyString_FromStringAndSize(l->start, pl);
306 306 }
307 307
308 308 static PyTypeObject lazymanifestKeysIterator = {
309 309 PyObject_HEAD_INIT(NULL)
310 310 0, /*ob_size */
311 311 "parsers.lazymanifest.keysiterator", /*tp_name */
312 312 sizeof(lmIter), /*tp_basicsize */
313 313 0, /*tp_itemsize */
314 314 lmiter_dealloc, /*tp_dealloc */
315 315 0, /*tp_print */
316 316 0, /*tp_getattr */
317 317 0, /*tp_setattr */
318 318 0, /*tp_compare */
319 319 0, /*tp_repr */
320 320 0, /*tp_as_number */
321 321 0, /*tp_as_sequence */
322 322 0, /*tp_as_mapping */
323 323 0, /*tp_hash */
324 324 0, /*tp_call */
325 325 0, /*tp_str */
326 326 0, /*tp_getattro */
327 327 0, /*tp_setattro */
328 328 0, /*tp_as_buffer */
329 329 /* tp_flags: Py_TPFLAGS_HAVE_ITER tells python to
330 330 use tp_iter and tp_iternext fields. */
331 331 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_ITER,
332 332 "Keys iterator for a lazymanifest.", /* tp_doc */
333 333 0, /* tp_traverse */
334 334 0, /* tp_clear */
335 335 0, /* tp_richcompare */
336 336 0, /* tp_weaklistoffset */
337 337 PyObject_SelfIter, /* tp_iter: __iter__() method */
338 338 lmiter_iterkeysnext, /* tp_iternext: next() method */
339 339 };
340 340
341 341 static lazymanifest *lazymanifest_copy(lazymanifest *self);
342 342
343 343 static PyObject *lazymanifest_getentriesiter(lazymanifest *self)
344 344 {
345 345 lmIter *i = NULL;
346 346 lazymanifest *t = lazymanifest_copy(self);
347 347 if (!t) {
348 348 PyErr_NoMemory();
349 349 return NULL;
350 350 }
351 351 i = PyObject_New(lmIter, &lazymanifestEntriesIterator);
352 352 if (i) {
353 353 i->m = t;
354 354 i->pos = -1;
355 355 } else {
356 356 Py_DECREF(t);
357 357 PyErr_NoMemory();
358 358 }
359 359 return (PyObject *)i;
360 360 }
361 361
362 362 static PyObject *lazymanifest_getkeysiter(lazymanifest *self)
363 363 {
364 364 lmIter *i = NULL;
365 365 lazymanifest *t = lazymanifest_copy(self);
366 366 if (!t) {
367 367 PyErr_NoMemory();
368 368 return NULL;
369 369 }
370 370 i = PyObject_New(lmIter, &lazymanifestKeysIterator);
371 371 if (i) {
372 372 i->m = t;
373 373 i->pos = -1;
374 374 } else {
375 375 Py_DECREF(t);
376 376 PyErr_NoMemory();
377 377 }
378 378 return (PyObject *)i;
379 379 }
380 380
381 381 /* __getitem__ and __setitem__ support */
382 382
383 383 static Py_ssize_t lazymanifest_size(lazymanifest *self)
384 384 {
385 385 return self->livelines;
386 386 }
387 387
388 388 static int linecmp(const void *left, const void *right)
389 389 {
390 390 return strcmp(((const line *)left)->start,
391 391 ((const line *)right)->start);
392 392 }
393 393
394 394 static PyObject *lazymanifest_getitem(lazymanifest *self, PyObject *key)
395 395 {
396 396 line needle;
397 397 line *hit;
398 398 if (!PyString_Check(key)) {
399 399 PyErr_Format(PyExc_TypeError,
400 400 "getitem: manifest keys must be a string.");
401 401 return NULL;
402 402 }
403 403 needle.start = PyString_AsString(key);
404 404 hit = bsearch(&needle, self->lines, self->numlines, sizeof(line),
405 405 &linecmp);
406 406 if (!hit || hit->deleted) {
407 407 PyErr_Format(PyExc_KeyError, "No such manifest entry.");
408 408 return NULL;
409 409 }
410 410 return hashflags(hit);
411 411 }
412 412
413 413 static int lazymanifest_delitem(lazymanifest *self, PyObject *key)
414 414 {
415 415 line needle;
416 416 line *hit;
417 417 if (!PyString_Check(key)) {
418 418 PyErr_Format(PyExc_TypeError,
419 419 "delitem: manifest keys must be a string.");
420 420 return -1;
421 421 }
422 422 needle.start = PyString_AsString(key);
423 423 hit = bsearch(&needle, self->lines, self->numlines, sizeof(line),
424 424 &linecmp);
425 425 if (!hit || hit->deleted) {
426 426 PyErr_Format(PyExc_KeyError,
427 427 "Tried to delete nonexistent manifest entry.");
428 428 return -1;
429 429 }
430 430 self->dirty = true;
431 431 hit->deleted = true;
432 432 self->livelines--;
433 433 return 0;
434 434 }
435 435
436 436 /* Do a binary search for the insertion point for new, creating the
437 437 * new entry if needed. */
438 438 static int internalsetitem(lazymanifest *self, line *new) {
439 439 int start = 0, end = self->numlines;
440 440 while (start < end) {
441 441 int pos = start + (end - start) / 2;
442 442 int c = linecmp(new, self->lines + pos);
443 443 if (c < 0)
444 444 end = pos;
445 445 else if (c > 0)
446 446 start = pos + 1;
447 447 else {
448 448 if (self->lines[pos].deleted)
449 449 self->livelines++;
450 450 start = pos;
451 451 goto finish;
452 452 }
453 453 }
454 454 /* being here means we need to do an insert */
455 455 if (!realloc_if_full(self)) {
456 456 PyErr_NoMemory();
457 457 return -1;
458 458 }
459 459 memmove(self->lines + start + 1, self->lines + start,
460 460 (self->numlines - start) * sizeof(line));
461 461 self->numlines++;
462 462 self->livelines++;
463 463 finish:
464 464 self->lines[start] = *new;
465 465 self->dirty = true;
466 466 return 0;
467 467 }
468 468
469 469 static int lazymanifest_setitem(
470 470 lazymanifest *self, PyObject *key, PyObject *value)
471 471 {
472 472 char *path;
473 473 Py_ssize_t plen;
474 474 PyObject *pyhash;
475 475 Py_ssize_t hlen;
476 476 char *hash;
477 477 PyObject *pyflags;
478 478 char *flags;
479 479 Py_ssize_t flen;
480 480 size_t dlen;
481 481 char *dest;
482 482 int i;
483 483 line new;
484 484 if (!PyString_Check(key)) {
485 485 PyErr_Format(PyExc_TypeError,
486 486 "setitem: manifest keys must be a string.");
487 487 return -1;
488 488 }
489 489 if (!value) {
490 490 return lazymanifest_delitem(self, key);
491 491 }
492 492 if (!PyTuple_Check(value) || PyTuple_Size(value) != 2) {
493 493 PyErr_Format(PyExc_TypeError,
494 494 "Manifest values must be a tuple of (node, flags).");
495 495 return -1;
496 496 }
497 497 if (PyString_AsStringAndSize(key, &path, &plen) == -1) {
498 498 return -1;
499 499 }
500 500
501 501 pyhash = PyTuple_GetItem(value, 0);
502 502 if (!PyString_Check(pyhash)) {
503 503 PyErr_Format(PyExc_TypeError,
504 504 "node must be a 20-byte string");
505 505 return -1;
506 506 }
507 507 hlen = PyString_Size(pyhash);
508 508 /* Some parts of the codebase try and set 21 or 22
509 509 * byte "hash" values in order to perturb things for
510 510 * status. We have to preserve at least the 21st
511 511 * byte. Sigh. If there's a 22nd byte, we drop it on
512 512 * the floor, which works fine.
513 513 */
514 514 if (hlen != 20 && hlen != 21 && hlen != 22) {
515 515 PyErr_Format(PyExc_TypeError,
516 516 "node must be a 20-byte string");
517 517 return -1;
518 518 }
519 519 hash = PyString_AsString(pyhash);
520 520
521 521 pyflags = PyTuple_GetItem(value, 1);
522 522 if (!PyString_Check(pyflags) || PyString_Size(pyflags) > 1) {
523 523 PyErr_Format(PyExc_TypeError,
524 524 "flags must a 0 or 1 byte string");
525 525 return -1;
526 526 }
527 527 if (PyString_AsStringAndSize(pyflags, &flags, &flen) == -1) {
528 528 return -1;
529 529 }
530 530 /* one null byte and one newline */
531 531 dlen = plen + 41 + flen + 1;
532 532 dest = malloc(dlen);
533 533 if (!dest) {
534 534 PyErr_NoMemory();
535 535 return -1;
536 536 }
537 537 memcpy(dest, path, plen + 1);
538 538 for (i = 0; i < 20; i++) {
539 539 /* Cast to unsigned, so it will not get sign-extended when promoted
540 540 * to int (as is done when passing to a variadic function)
541 541 */
542 542 sprintf(dest + plen + 1 + (i * 2), "%02x", (unsigned char)hash[i]);
543 543 }
544 544 memcpy(dest + plen + 41, flags, flen);
545 545 dest[plen + 41 + flen] = '\n';
546 546 new.start = dest;
547 547 new.len = dlen;
548 548 new.hash_suffix = '\0';
549 549 if (hlen > 20) {
550 550 new.hash_suffix = hash[20];
551 551 }
552 552 new.from_malloc = true; /* is `start` a pointer we allocated? */
553 553 new.deleted = false; /* is this entry deleted? */
554 554 if (internalsetitem(self, &new)) {
555 555 return -1;
556 556 }
557 557 return 0;
558 558 }
559 559
560 560 static PyMappingMethods lazymanifest_mapping_methods = {
561 561 (lenfunc)lazymanifest_size, /* mp_length */
562 562 (binaryfunc)lazymanifest_getitem, /* mp_subscript */
563 563 (objobjargproc)lazymanifest_setitem, /* mp_ass_subscript */
564 564 };
565 565
566 566 /* sequence methods (important or __contains__ builds an iterator */
567 567
568 568 static int lazymanifest_contains(lazymanifest *self, PyObject *key)
569 569 {
570 570 line needle;
571 571 line *hit;
572 572 if (!PyString_Check(key)) {
573 573 /* Our keys are always strings, so if the contains
574 574 * check is for a non-string, just return false. */
575 575 return 0;
576 576 }
577 577 needle.start = PyString_AsString(key);
578 578 hit = bsearch(&needle, self->lines, self->numlines, sizeof(line),
579 579 &linecmp);
580 580 if (!hit || hit->deleted) {
581 581 return 0;
582 582 }
583 583 return 1;
584 584 }
585 585
586 586 static PySequenceMethods lazymanifest_seq_meths = {
587 587 (lenfunc)lazymanifest_size, /* sq_length */
588 588 0, /* sq_concat */
589 589 0, /* sq_repeat */
590 590 0, /* sq_item */
591 591 0, /* sq_slice */
592 592 0, /* sq_ass_item */
593 593 0, /* sq_ass_slice */
594 594 (objobjproc)lazymanifest_contains, /* sq_contains */
595 595 0, /* sq_inplace_concat */
596 596 0, /* sq_inplace_repeat */
597 597 };
598 598
599 599
600 600 /* Other methods (copy, diff, etc) */
601 601 static PyTypeObject lazymanifestType;
602 602
603 603 /* If the manifest has changes, build the new manifest text and reindex it. */
604 604 static int compact(lazymanifest *self) {
605 605 int i;
606 606 ssize_t need = 0;
607 607 char *data;
608 608 line *src, *dst;
609 609 PyObject *pydata;
610 610 if (!self->dirty)
611 611 return 0;
612 612 for (i = 0; i < self->numlines; i++) {
613 613 if (!self->lines[i].deleted) {
614 614 need += self->lines[i].len;
615 615 }
616 616 }
617 617 pydata = PyString_FromStringAndSize(NULL, need);
618 618 if (!pydata)
619 619 return -1;
620 620 data = PyString_AsString(pydata);
621 621 if (!data) {
622 622 return -1;
623 623 }
624 624 src = self->lines;
625 625 dst = self->lines;
626 626 for (i = 0; i < self->numlines; i++, src++) {
627 627 char *tofree = NULL;
628 628 if (src->from_malloc) {
629 629 tofree = src->start;
630 630 }
631 631 if (!src->deleted) {
632 632 memcpy(data, src->start, src->len);
633 633 *dst = *src;
634 634 dst->start = data;
635 635 dst->from_malloc = false;
636 636 data += dst->len;
637 637 dst++;
638 638 }
639 639 free(tofree);
640 640 }
641 641 Py_DECREF(self->pydata);
642 642 self->pydata = pydata;
643 643 self->numlines = self->livelines;
644 644 self->dirty = false;
645 645 return 0;
646 646 }
647 647
648 648 static PyObject *lazymanifest_text(lazymanifest *self)
649 649 {
650 650 if (compact(self) != 0) {
651 651 PyErr_NoMemory();
652 652 return NULL;
653 653 }
654 654 Py_INCREF(self->pydata);
655 655 return self->pydata;
656 656 }
657 657
658 658 static lazymanifest *lazymanifest_copy(lazymanifest *self)
659 659 {
660 660 lazymanifest *copy = NULL;
661 661 if (compact(self) != 0) {
662 662 goto nomem;
663 663 }
664 664 copy = PyObject_New(lazymanifest, &lazymanifestType);
665 665 if (!copy) {
666 666 goto nomem;
667 667 }
668 668 copy->numlines = self->numlines;
669 669 copy->livelines = self->livelines;
670 670 copy->dirty = false;
671 671 copy->lines = malloc(self->maxlines *sizeof(line));
672 672 if (!copy->lines) {
673 673 goto nomem;
674 674 }
675 675 memcpy(copy->lines, self->lines, self->numlines * sizeof(line));
676 676 copy->maxlines = self->maxlines;
677 677 copy->pydata = self->pydata;
678 678 Py_INCREF(copy->pydata);
679 679 return copy;
680 680 nomem:
681 681 PyErr_NoMemory();
682 682 Py_XDECREF(copy);
683 683 return NULL;
684 684 }
685 685
686 686 static lazymanifest *lazymanifest_filtercopy(
687 687 lazymanifest *self, PyObject *matchfn)
688 688 {
689 689 lazymanifest *copy = NULL;
690 690 int i;
691 691 if (!PyCallable_Check(matchfn)) {
692 692 PyErr_SetString(PyExc_TypeError, "matchfn must be callable");
693 693 return NULL;
694 694 }
695 695 /* compact ourselves first to avoid double-frees later when we
696 696 * compact tmp so that it doesn't have random pointers to our
697 697 * underlying from_malloc-data (self->pydata is safe) */
698 698 if (compact(self) != 0) {
699 699 goto nomem;
700 700 }
701 701 copy = PyObject_New(lazymanifest, &lazymanifestType);
702 702 copy->dirty = true;
703 703 copy->lines = malloc(self->maxlines * sizeof(line));
704 704 if (!copy->lines) {
705 705 goto nomem;
706 706 }
707 707 copy->maxlines = self->maxlines;
708 708 copy->numlines = 0;
709 709 copy->pydata = self->pydata;
710 710 Py_INCREF(self->pydata);
711 711 for (i = 0; i < self->numlines; i++) {
712 712 PyObject *arg = PyString_FromString(self->lines[i].start);
713 713 PyObject *arglist = PyTuple_Pack(1, arg);
714 714 PyObject *result = PyObject_CallObject(matchfn, arglist);
715 715 Py_DECREF(arglist);
716 716 Py_DECREF(arg);
717 717 /* if the callback raised an exception, just let it
718 718 * through and give up */
719 719 if (!result) {
720 720 free(copy->lines);
721 721 Py_DECREF(self->pydata);
722 722 return NULL;
723 723 }
724 724 if (PyObject_IsTrue(result)) {
725 725 assert(!(self->lines[i].from_malloc));
726 726 copy->lines[copy->numlines++] = self->lines[i];
727 727 }
728 728 Py_DECREF(result);
729 729 }
730 730 copy->livelines = copy->numlines;
731 731 return copy;
732 732 nomem:
733 733 PyErr_NoMemory();
734 734 Py_XDECREF(copy);
735 735 return NULL;
736 736 }
737 737
738 738 static PyObject *lazymanifest_diff(lazymanifest *self, PyObject *args)
739 739 {
740 740 lazymanifest *other;
741 741 PyObject *pyclean = NULL;
742 742 bool listclean;
743 743 PyObject *emptyTup = NULL, *ret = NULL;
744 744 PyObject *es;
745 745 int sneedle = 0, oneedle = 0;
746 746 if (!PyArg_ParseTuple(args, "O!|O", &lazymanifestType, &other, &pyclean)) {
747 747 return NULL;
748 748 }
749 749 listclean = (!pyclean) ? false : PyObject_IsTrue(pyclean);
750 750 es = PyString_FromString("");
751 751 if (!es) {
752 752 goto nomem;
753 753 }
754 754 emptyTup = PyTuple_Pack(2, Py_None, es);
755 755 Py_DECREF(es);
756 756 if (!emptyTup) {
757 757 goto nomem;
758 758 }
759 759 ret = PyDict_New();
760 760 if (!ret) {
761 761 goto nomem;
762 762 }
763 763 while (sneedle != self->numlines || oneedle != other->numlines) {
764 764 line *left = self->lines + sneedle;
765 765 line *right = other->lines + oneedle;
766 766 int result;
767 767 PyObject *key;
768 768 PyObject *outer;
769 769 /* If we're looking at a deleted entry and it's not
770 770 * the end of the manifest, just skip it. */
771 771 if (left->deleted && sneedle < self->numlines) {
772 772 sneedle++;
773 773 continue;
774 774 }
775 775 if (right->deleted && oneedle < other->numlines) {
776 776 oneedle++;
777 777 continue;
778 778 }
779 779 /* if we're at the end of either manifest, then we
780 780 * know the remaining items are adds so we can skip
781 781 * the strcmp. */
782 782 if (sneedle == self->numlines) {
783 783 result = 1;
784 784 } else if (oneedle == other->numlines) {
785 785 result = -1;
786 786 } else {
787 787 result = linecmp(left, right);
788 788 }
789 789 key = result <= 0 ?
790 790 PyString_FromString(left->start) :
791 791 PyString_FromString(right->start);
792 792 if (!key)
793 793 goto nomem;
794 794 if (result < 0) {
795 795 PyObject *l = hashflags(left);
796 796 if (!l) {
797 797 goto nomem;
798 798 }
799 799 outer = PyTuple_Pack(2, l, emptyTup);
800 800 Py_DECREF(l);
801 801 if (!outer) {
802 802 goto nomem;
803 803 }
804 804 PyDict_SetItem(ret, key, outer);
805 805 Py_DECREF(outer);
806 806 sneedle++;
807 807 } else if (result > 0) {
808 808 PyObject *r = hashflags(right);
809 809 if (!r) {
810 810 goto nomem;
811 811 }
812 812 outer = PyTuple_Pack(2, emptyTup, r);
813 813 Py_DECREF(r);
814 814 if (!outer) {
815 815 goto nomem;
816 816 }
817 817 PyDict_SetItem(ret, key, outer);
818 818 Py_DECREF(outer);
819 819 oneedle++;
820 820 } else {
821 821 /* file exists in both manifests */
822 822 if (left->len != right->len
823 823 || memcmp(left->start, right->start, left->len)
824 824 || left->hash_suffix != right->hash_suffix) {
825 825 PyObject *l = hashflags(left);
826 826 PyObject *r;
827 827 if (!l) {
828 828 goto nomem;
829 829 }
830 830 r = hashflags(right);
831 831 if (!r) {
832 832 Py_DECREF(l);
833 833 goto nomem;
834 834 }
835 835 outer = PyTuple_Pack(2, l, r);
836 836 Py_DECREF(l);
837 837 Py_DECREF(r);
838 838 if (!outer) {
839 839 goto nomem;
840 840 }
841 841 PyDict_SetItem(ret, key, outer);
842 842 Py_DECREF(outer);
843 843 } else if (listclean) {
844 844 PyDict_SetItem(ret, key, Py_None);
845 845 }
846 846 sneedle++;
847 847 oneedle++;
848 848 }
849 849 Py_DECREF(key);
850 850 }
851 851 Py_DECREF(emptyTup);
852 852 return ret;
853 853 nomem:
854 854 PyErr_NoMemory();
855 855 Py_XDECREF(ret);
856 856 Py_XDECREF(emptyTup);
857 857 return NULL;
858 858 }
859 859
860 860 static PyMethodDef lazymanifest_methods[] = {
861 861 {"iterkeys", (PyCFunction)lazymanifest_getkeysiter, METH_NOARGS,
862 862 "Iterate over file names in this lazymanifest."},
863 863 {"iterentries", (PyCFunction)lazymanifest_getentriesiter, METH_NOARGS,
864 864 "Iterate over (path, nodeid, flags) typles in this lazymanifest."},
865 865 {"copy", (PyCFunction)lazymanifest_copy, METH_NOARGS,
866 866 "Make a copy of this lazymanifest."},
867 867 {"filtercopy", (PyCFunction)lazymanifest_filtercopy, METH_O,
868 868 "Make a copy of this manifest filtered by matchfn."},
869 869 {"diff", (PyCFunction)lazymanifest_diff, METH_VARARGS,
870 870 "Compare this lazymanifest to another one."},
871 871 {"text", (PyCFunction)lazymanifest_text, METH_NOARGS,
872 872 "Encode this manifest to text."},
873 873 {NULL},
874 874 };
875 875
876 876 static PyTypeObject lazymanifestType = {
877 877 PyObject_HEAD_INIT(NULL)
878 878 0, /* ob_size */
879 879 "parsers.lazymanifest", /* tp_name */
880 880 sizeof(lazymanifest), /* tp_basicsize */
881 881 0, /* tp_itemsize */
882 882 (destructor)lazymanifest_dealloc, /* tp_dealloc */
883 883 0, /* tp_print */
884 884 0, /* tp_getattr */
885 885 0, /* tp_setattr */
886 886 0, /* tp_compare */
887 887 0, /* tp_repr */
888 888 0, /* tp_as_number */
889 889 &lazymanifest_seq_meths, /* tp_as_sequence */
890 890 &lazymanifest_mapping_methods, /* tp_as_mapping */
891 891 0, /* tp_hash */
892 892 0, /* tp_call */
893 893 0, /* tp_str */
894 894 0, /* tp_getattro */
895 895 0, /* tp_setattro */
896 896 0, /* tp_as_buffer */
897 897 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_SEQUENCE_IN, /* tp_flags */
898 898 "TODO(augie)", /* tp_doc */
899 899 0, /* tp_traverse */
900 900 0, /* tp_clear */
901 901 0, /* tp_richcompare */
902 902 0, /* tp_weaklistoffset */
903 903 (getiterfunc)lazymanifest_getkeysiter, /* tp_iter */
904 904 0, /* tp_iternext */
905 905 lazymanifest_methods, /* tp_methods */
906 906 0, /* tp_members */
907 907 0, /* tp_getset */
908 908 0, /* tp_base */
909 909 0, /* tp_dict */
910 910 0, /* tp_descr_get */
911 911 0, /* tp_descr_set */
912 912 0, /* tp_dictoffset */
913 913 (initproc)lazymanifest_init, /* tp_init */
914 914 0, /* tp_alloc */
915 915 };
916 916
917 917 void manifest_module_init(PyObject * mod)
918 918 {
919 919 lazymanifestType.tp_new = PyType_GenericNew;
920 920 if (PyType_Ready(&lazymanifestType) < 0)
921 921 return;
922 922 Py_INCREF(&lazymanifestType);
923 923
924 924 PyModule_AddObject(mod, "lazymanifest",
925 925 (PyObject *)&lazymanifestType);
926 926 }
General Comments 0
You need to be logged in to leave comments. Login now