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