comparison mercurial/cext/manifest.c @ 32372:df448de7cf3b

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