##// END OF EJS Templates
lazymanifest: don't depend on printf's 'hh' format to work...
Martin von Zweigbergk -
r24286:40528ad1 default
parent child Browse files
Show More
@@ -1,849 +1,852
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 <assert.h>
10 10 #include <string.h>
11 11 #include <stdlib.h>
12 12
13 13 #include <Python.h>
14 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 PyObject *lmiter_iternext(PyObject *o)
226 226 {
227 227 size_t pl;
228 228 line *l;
229 229 Py_ssize_t consumed;
230 230 PyObject *ret = NULL, *path = NULL, *hash = NULL, *flags = NULL;
231 231 lmIter *self = (lmIter *)o;
232 232 do {
233 233 self->pos++;
234 234 if (self->pos >= self->m->numlines) {
235 235 goto bail;
236 236 }
237 237 /* skip over deleted manifest entries */
238 238 } while (self->m->lines[self->pos].deleted);
239 239 l = self->m->lines + self->pos;
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 (!flags) {
247 247 goto bail;
248 248 }
249 249 ret = PyTuple_Pack(3, path, hash, flags);
250 250 bail:
251 251 Py_XDECREF(path);
252 252 Py_XDECREF(hash);
253 253 Py_XDECREF(flags);
254 254 return ret;
255 255 }
256 256
257 257 static PyTypeObject lazymanifestIterator = {
258 258 PyObject_HEAD_INIT(NULL)
259 259 0, /*ob_size */
260 260 "parsers.lazymanifest.iterator", /*tp_name */
261 261 sizeof(lmIter), /*tp_basicsize */
262 262 0, /*tp_itemsize */
263 263 lmiter_dealloc, /*tp_dealloc */
264 264 0, /*tp_print */
265 265 0, /*tp_getattr */
266 266 0, /*tp_setattr */
267 267 0, /*tp_compare */
268 268 0, /*tp_repr */
269 269 0, /*tp_as_number */
270 270 0, /*tp_as_sequence */
271 271 0, /*tp_as_mapping */
272 272 0, /*tp_hash */
273 273 0, /*tp_call */
274 274 0, /*tp_str */
275 275 0, /*tp_getattro */
276 276 0, /*tp_setattro */
277 277 0, /*tp_as_buffer */
278 278 /* tp_flags: Py_TPFLAGS_HAVE_ITER tells python to
279 279 use tp_iter and tp_iternext fields. */
280 280 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_ITER,
281 281 "Iterator for a lazymanifest.", /* tp_doc */
282 282 0, /* tp_traverse */
283 283 0, /* tp_clear */
284 284 0, /* tp_richcompare */
285 285 0, /* tp_weaklistoffset */
286 286 PyObject_SelfIter, /* tp_iter: __iter__() method */
287 287 lmiter_iternext, /* tp_iternext: next() method */
288 288 };
289 289
290 290 static lazymanifest *lazymanifest_copy(lazymanifest *self);
291 291
292 292 static PyObject *lazymanifest_getiter(lazymanifest *self)
293 293 {
294 294 lmIter *i = NULL;
295 295 lazymanifest *t = lazymanifest_copy(self);
296 296 if (!t) {
297 297 PyErr_NoMemory();
298 298 return NULL;
299 299 }
300 300 i = PyObject_New(lmIter, &lazymanifestIterator);
301 301 if (i) {
302 302 i->m = t;
303 303 i->pos = -1;
304 304 } else {
305 305 Py_DECREF(t);
306 306 PyErr_NoMemory();
307 307 }
308 308 return (PyObject *)i;
309 309 }
310 310
311 311 /* __getitem__ and __setitem__ support */
312 312
313 313 static Py_ssize_t lazymanifest_size(lazymanifest *self)
314 314 {
315 315 return self->livelines;
316 316 }
317 317
318 318 static int linecmp(const void *left, const void *right)
319 319 {
320 320 return strcmp(((const line *)left)->start,
321 321 ((const line *)right)->start);
322 322 }
323 323
324 324 static PyObject *lazymanifest_getitem(lazymanifest *self, PyObject *key)
325 325 {
326 326 line needle;
327 327 line *hit;
328 328 if (!PyString_Check(key)) {
329 329 PyErr_Format(PyExc_TypeError,
330 330 "getitem: manifest keys must be a string.");
331 331 return NULL;
332 332 }
333 333 needle.start = PyString_AsString(key);
334 334 hit = bsearch(&needle, self->lines, self->numlines, sizeof(line),
335 335 &linecmp);
336 336 if (!hit || hit->deleted) {
337 337 PyErr_Format(PyExc_KeyError, "No such manifest entry.");
338 338 return NULL;
339 339 }
340 340 return hashflags(hit);
341 341 }
342 342
343 343 static int lazymanifest_delitem(lazymanifest *self, PyObject *key)
344 344 {
345 345 line needle;
346 346 line *hit;
347 347 if (!PyString_Check(key)) {
348 348 PyErr_Format(PyExc_TypeError,
349 349 "delitem: manifest keys must be a string.");
350 350 return -1;
351 351 }
352 352 needle.start = PyString_AsString(key);
353 353 hit = bsearch(&needle, self->lines, self->numlines, sizeof(line),
354 354 &linecmp);
355 355 if (!hit || hit->deleted) {
356 356 PyErr_Format(PyExc_KeyError,
357 357 "Tried to delete nonexistent manifest entry.");
358 358 return -1;
359 359 }
360 360 self->dirty = true;
361 361 hit->deleted = true;
362 362 self->livelines--;
363 363 return 0;
364 364 }
365 365
366 366 /* Do a binary search for the insertion point for new, creating the
367 367 * new entry if needed. */
368 368 static int internalsetitem(lazymanifest *self, line *new) {
369 369 int start = 0, end = self->numlines;
370 370 while (start < end) {
371 371 int pos = start + (end - start) / 2;
372 372 int c = linecmp(new, self->lines + pos);
373 373 if (c < 0)
374 374 end = pos;
375 375 else if (c > 0)
376 376 start = pos + 1;
377 377 else {
378 378 if (self->lines[pos].deleted)
379 379 self->livelines++;
380 380 start = pos;
381 381 goto finish;
382 382 }
383 383 }
384 384 /* being here means we need to do an insert */
385 385 if (!realloc_if_full(self)) {
386 386 PyErr_NoMemory();
387 387 return -1;
388 388 }
389 389 memmove(self->lines + start + 1, self->lines + start,
390 390 (self->numlines - start) * sizeof(line));
391 391 self->numlines++;
392 392 self->livelines++;
393 393 finish:
394 394 self->lines[start] = *new;
395 395 self->dirty = true;
396 396 return 0;
397 397 }
398 398
399 399 static int lazymanifest_setitem(
400 400 lazymanifest *self, PyObject *key, PyObject *value)
401 401 {
402 402 char *path;
403 403 Py_ssize_t plen;
404 404 PyObject *pyhash;
405 405 Py_ssize_t hlen;
406 406 char *hash;
407 407 PyObject *pyflags;
408 408 char *flags;
409 409 Py_ssize_t flen;
410 410 size_t dlen;
411 411 char *dest;
412 412 int i;
413 413 line new;
414 414 if (!PyString_Check(key)) {
415 415 PyErr_Format(PyExc_TypeError,
416 416 "setitem: manifest keys must be a string.");
417 417 return -1;
418 418 }
419 419 if (!value) {
420 420 return lazymanifest_delitem(self, key);
421 421 }
422 422 if (!PyTuple_Check(value) || PyTuple_Size(value) != 2) {
423 423 PyErr_Format(PyExc_TypeError,
424 424 "Manifest values must be a tuple of (node, flags).");
425 425 return -1;
426 426 }
427 427 if (PyString_AsStringAndSize(key, &path, &plen) == -1) {
428 428 return -1;
429 429 }
430 430
431 431 pyhash = PyTuple_GetItem(value, 0);
432 432 if (!PyString_Check(pyhash)) {
433 433 PyErr_Format(PyExc_TypeError,
434 434 "node must be a 20-byte string");
435 435 return -1;
436 436 }
437 437 hlen = PyString_Size(pyhash);
438 438 /* Some parts of the codebase try and set 21 or 22
439 439 * byte "hash" values in order to perturb things for
440 440 * status. We have to preserve at least the 21st
441 441 * byte. Sigh. If there's a 22nd byte, we drop it on
442 442 * the floor, which works fine.
443 443 */
444 444 if (hlen != 20 && hlen != 21 && hlen != 22) {
445 445 PyErr_Format(PyExc_TypeError,
446 446 "node must be a 20-byte string");
447 447 return -1;
448 448 }
449 449 hash = PyString_AsString(pyhash);
450 450
451 451 pyflags = PyTuple_GetItem(value, 1);
452 452 if (!PyString_Check(pyflags) || PyString_Size(pyflags) > 1) {
453 453 PyErr_Format(PyExc_TypeError,
454 454 "flags must a 0 or 1 byte string");
455 455 return -1;
456 456 }
457 457 if (PyString_AsStringAndSize(pyflags, &flags, &flen) == -1) {
458 458 return -1;
459 459 }
460 460 /* one null byte and one newline */
461 461 dlen = plen + 41 + flen + 1;
462 462 dest = malloc(dlen);
463 463 if (!dest) {
464 464 PyErr_NoMemory();
465 465 return -1;
466 466 }
467 467 memcpy(dest, path, plen + 1);
468 468 for (i = 0; i < 20; i++) {
469 sprintf(dest + plen + 1 + (i * 2), "%02hhx", hash[i]);
469 /* Cast to unsigned, so it will not get sign-extended when promoted
470 * to int (as is done when passing to a variadic function)
471 */
472 sprintf(dest + plen + 1 + (i * 2), "%02x", (unsigned char)hash[i]);
470 473 }
471 474 memcpy(dest + plen + 41, flags, flen);
472 475 dest[plen + 41 + flen] = '\n';
473 476 new.start = dest;
474 477 new.len = dlen;
475 478 new.hash_suffix = '\0';
476 479 if (hlen > 20) {
477 480 new.hash_suffix = hash[20];
478 481 }
479 482 new.from_malloc = true; /* is `start` a pointer we allocated? */
480 483 new.deleted = false; /* is this entry deleted? */
481 484 if (internalsetitem(self, &new)) {
482 485 return -1;
483 486 }
484 487 return 0;
485 488 }
486 489
487 490 static PyMappingMethods lazymanifest_mapping_methods = {
488 491 (lenfunc)lazymanifest_size, /* mp_length */
489 492 (binaryfunc)lazymanifest_getitem, /* mp_subscript */
490 493 (objobjargproc)lazymanifest_setitem, /* mp_ass_subscript */
491 494 };
492 495
493 496 /* sequence methods (important or __contains__ builds an iterator */
494 497
495 498 static int lazymanifest_contains(lazymanifest *self, PyObject *key)
496 499 {
497 500 line needle;
498 501 line *hit;
499 502 if (!PyString_Check(key)) {
500 503 /* Our keys are always strings, so if the contains
501 504 * check is for a non-string, just return false. */
502 505 return 0;
503 506 }
504 507 needle.start = PyString_AsString(key);
505 508 hit = bsearch(&needle, self->lines, self->numlines, sizeof(line),
506 509 &linecmp);
507 510 if (!hit || hit->deleted) {
508 511 return 0;
509 512 }
510 513 return 1;
511 514 }
512 515
513 516 static PySequenceMethods lazymanifest_seq_meths = {
514 517 (lenfunc)lazymanifest_size, /* sq_length */
515 518 0, /* sq_concat */
516 519 0, /* sq_repeat */
517 520 0, /* sq_item */
518 521 0, /* sq_slice */
519 522 0, /* sq_ass_item */
520 523 0, /* sq_ass_slice */
521 524 (objobjproc)lazymanifest_contains, /* sq_contains */
522 525 0, /* sq_inplace_concat */
523 526 0, /* sq_inplace_repeat */
524 527 };
525 528
526 529
527 530 /* Other methods (copy, diff, etc) */
528 531 static PyTypeObject lazymanifestType;
529 532
530 533 /* If the manifest has changes, build the new manifest text and reindex it. */
531 534 static int compact(lazymanifest *self) {
532 535 int i;
533 536 ssize_t need = 0;
534 537 char *data;
535 538 line *src, *dst;
536 539 PyObject *pydata;
537 540 if (!self->dirty)
538 541 return 0;
539 542 for (i = 0; i < self->numlines; i++) {
540 543 if (!self->lines[i].deleted) {
541 544 need += self->lines[i].len;
542 545 }
543 546 }
544 547 pydata = PyString_FromStringAndSize(NULL, need);
545 548 if (!pydata)
546 549 return -1;
547 550 data = PyString_AsString(pydata);
548 551 if (!data) {
549 552 return -1;
550 553 }
551 554 src = self->lines;
552 555 dst = self->lines;
553 556 for (i = 0; i < self->numlines; i++, src++) {
554 557 char *tofree = NULL;
555 558 if (src->from_malloc) {
556 559 tofree = src->start;
557 560 }
558 561 if (!src->deleted) {
559 562 memcpy(data, src->start, src->len);
560 563 *dst = *src;
561 564 dst->start = data;
562 565 dst->from_malloc = false;
563 566 data += dst->len;
564 567 dst++;
565 568 }
566 569 free(tofree);
567 570 }
568 571 Py_DECREF(self->pydata);
569 572 self->pydata = pydata;
570 573 self->numlines = self->livelines;
571 574 self->dirty = false;
572 575 return 0;
573 576 }
574 577
575 578 static PyObject *lazymanifest_text(lazymanifest *self)
576 579 {
577 580 if (compact(self) != 0) {
578 581 PyErr_NoMemory();
579 582 return NULL;
580 583 }
581 584 Py_INCREF(self->pydata);
582 585 return self->pydata;
583 586 }
584 587
585 588 static lazymanifest *lazymanifest_copy(lazymanifest *self)
586 589 {
587 590 lazymanifest *copy = NULL;
588 591 if (compact(self) != 0) {
589 592 goto nomem;
590 593 }
591 594 copy = PyObject_New(lazymanifest, &lazymanifestType);
592 595 if (!copy) {
593 596 goto nomem;
594 597 }
595 598 copy->numlines = self->numlines;
596 599 copy->livelines = self->livelines;
597 600 copy->dirty = false;
598 601 copy->lines = malloc(self->maxlines *sizeof(line));
599 602 if (!copy->lines) {
600 603 goto nomem;
601 604 }
602 605 memcpy(copy->lines, self->lines, self->numlines * sizeof(line));
603 606 copy->maxlines = self->maxlines;
604 607 copy->pydata = self->pydata;
605 608 Py_INCREF(copy->pydata);
606 609 return copy;
607 610 nomem:
608 611 PyErr_NoMemory();
609 612 Py_XDECREF(copy);
610 613 return NULL;
611 614 }
612 615
613 616 static lazymanifest *lazymanifest_filtercopy(
614 617 lazymanifest *self, PyObject *matchfn)
615 618 {
616 619 lazymanifest *copy = NULL;
617 620 int i;
618 621 if (!PyCallable_Check(matchfn)) {
619 622 PyErr_SetString(PyExc_TypeError, "matchfn must be callable");
620 623 return NULL;
621 624 }
622 625 /* compact ourselves first to avoid double-frees later when we
623 626 * compact tmp so that it doesn't have random pointers to our
624 627 * underlying from_malloc-data (self->pydata is safe) */
625 628 if (compact(self) != 0) {
626 629 goto nomem;
627 630 }
628 631 copy = PyObject_New(lazymanifest, &lazymanifestType);
629 632 copy->dirty = true;
630 633 copy->lines = malloc(self->maxlines * sizeof(line));
631 634 if (!copy->lines) {
632 635 goto nomem;
633 636 }
634 637 copy->maxlines = self->maxlines;
635 638 copy->numlines = 0;
636 639 copy->pydata = self->pydata;
637 640 Py_INCREF(self->pydata);
638 641 for (i = 0; i < self->numlines; i++) {
639 642 PyObject *arg = PyString_FromString(self->lines[i].start);
640 643 PyObject *arglist = PyTuple_Pack(1, arg);
641 644 PyObject *result = PyObject_CallObject(matchfn, arglist);
642 645 Py_DECREF(arglist);
643 646 Py_DECREF(arg);
644 647 /* if the callback raised an exception, just let it
645 648 * through and give up */
646 649 if (!result) {
647 650 free(copy->lines);
648 651 Py_DECREF(self->pydata);
649 652 return NULL;
650 653 }
651 654 if (PyObject_IsTrue(result)) {
652 655 assert(!(self->lines[i].from_malloc));
653 656 copy->lines[copy->numlines++] = self->lines[i];
654 657 }
655 658 Py_DECREF(result);
656 659 }
657 660 copy->livelines = copy->numlines;
658 661 return copy;
659 662 nomem:
660 663 PyErr_NoMemory();
661 664 Py_XDECREF(copy);
662 665 return NULL;
663 666 }
664 667
665 668 static PyObject *lazymanifest_diff(lazymanifest *self, PyObject *args)
666 669 {
667 670 lazymanifest *other;
668 671 PyObject *pyclean = NULL;
669 672 bool listclean;
670 673 PyObject *emptyTup = NULL, *ret = NULL;
671 674 PyObject *es;
672 675 int sneedle = 0, oneedle = 0;
673 676 if (!PyArg_ParseTuple(args, "O!|O", &lazymanifestType, &other, &pyclean)) {
674 677 return NULL;
675 678 }
676 679 listclean = (!pyclean) ? false : PyObject_IsTrue(pyclean);
677 680 es = PyString_FromString("");
678 681 if (!es) {
679 682 goto nomem;
680 683 }
681 684 emptyTup = PyTuple_Pack(2, Py_None, es);
682 685 Py_DECREF(es);
683 686 if (!emptyTup) {
684 687 goto nomem;
685 688 }
686 689 ret = PyDict_New();
687 690 if (!ret) {
688 691 goto nomem;
689 692 }
690 693 while (sneedle != self->numlines || oneedle != other->numlines) {
691 694 line *left = self->lines + sneedle;
692 695 line *right = other->lines + oneedle;
693 696 int result;
694 697 PyObject *key;
695 698 PyObject *outer;
696 699 /* If we're looking at a deleted entry and it's not
697 700 * the end of the manifest, just skip it. */
698 701 if (left->deleted && sneedle < self->numlines) {
699 702 sneedle++;
700 703 continue;
701 704 }
702 705 if (right->deleted && oneedle < other->numlines) {
703 706 oneedle++;
704 707 continue;
705 708 }
706 709 /* if we're at the end of either manifest, then we
707 710 * know the remaining items are adds so we can skip
708 711 * the strcmp. */
709 712 if (sneedle == self->numlines) {
710 713 result = 1;
711 714 } else if (oneedle == other->numlines) {
712 715 result = -1;
713 716 } else {
714 717 result = linecmp(left, right);
715 718 }
716 719 key = result <= 0 ?
717 720 PyString_FromString(left->start) :
718 721 PyString_FromString(right->start);
719 722 if (!key)
720 723 goto nomem;
721 724 if (result < 0) {
722 725 PyObject *l = hashflags(left);
723 726 if (!l) {
724 727 goto nomem;
725 728 }
726 729 outer = PyTuple_Pack(2, l, emptyTup);
727 730 Py_DECREF(l);
728 731 if (!outer) {
729 732 goto nomem;
730 733 }
731 734 PyDict_SetItem(ret, key, outer);
732 735 Py_DECREF(outer);
733 736 sneedle++;
734 737 } else if (result > 0) {
735 738 PyObject *r = hashflags(right);
736 739 if (!r) {
737 740 goto nomem;
738 741 }
739 742 outer = PyTuple_Pack(2, emptyTup, r);
740 743 Py_DECREF(r);
741 744 if (!outer) {
742 745 goto nomem;
743 746 }
744 747 PyDict_SetItem(ret, key, outer);
745 748 Py_DECREF(outer);
746 749 oneedle++;
747 750 } else {
748 751 /* file exists in both manifests */
749 752 if (left->len != right->len
750 753 || memcmp(left->start, right->start, left->len)
751 754 || left->hash_suffix != right->hash_suffix) {
752 755 PyObject *l = hashflags(left);
753 756 PyObject *r;
754 757 if (!l) {
755 758 goto nomem;
756 759 }
757 760 r = hashflags(right);
758 761 if (!r) {
759 762 Py_DECREF(l);
760 763 goto nomem;
761 764 }
762 765 outer = PyTuple_Pack(2, l, r);
763 766 Py_DECREF(l);
764 767 Py_DECREF(r);
765 768 if (!outer) {
766 769 goto nomem;
767 770 }
768 771 PyDict_SetItem(ret, key, outer);
769 772 Py_DECREF(outer);
770 773 } else if (listclean) {
771 774 PyDict_SetItem(ret, key, Py_None);
772 775 }
773 776 sneedle++;
774 777 oneedle++;
775 778 }
776 779 Py_DECREF(key);
777 780 }
778 781 Py_DECREF(emptyTup);
779 782 return ret;
780 783 nomem:
781 784 PyErr_NoMemory();
782 785 Py_XDECREF(ret);
783 786 Py_XDECREF(emptyTup);
784 787 return NULL;
785 788 }
786 789
787 790 static PyMethodDef lazymanifest_methods[] = {
788 791 {"copy", (PyCFunction)lazymanifest_copy, METH_NOARGS,
789 792 "Make a copy of this lazymanifest."},
790 793 {"filtercopy", (PyCFunction)lazymanifest_filtercopy, METH_O,
791 794 "Make a copy of this manifest filtered by matchfn."},
792 795 {"diff", (PyCFunction)lazymanifest_diff, METH_VARARGS,
793 796 "Compare this lazymanifest to another one."},
794 797 {"text", (PyCFunction)lazymanifest_text, METH_NOARGS,
795 798 "Encode this manifest to text."},
796 799 {NULL},
797 800 };
798 801
799 802 static PyTypeObject lazymanifestType = {
800 803 PyObject_HEAD_INIT(NULL)
801 804 0, /* ob_size */
802 805 "parsers.lazymanifest", /* tp_name */
803 806 sizeof(lazymanifest), /* tp_basicsize */
804 807 0, /* tp_itemsize */
805 808 (destructor)lazymanifest_dealloc, /* tp_dealloc */
806 809 0, /* tp_print */
807 810 0, /* tp_getattr */
808 811 0, /* tp_setattr */
809 812 0, /* tp_compare */
810 813 0, /* tp_repr */
811 814 0, /* tp_as_number */
812 815 &lazymanifest_seq_meths, /* tp_as_sequence */
813 816 &lazymanifest_mapping_methods, /* tp_as_mapping */
814 817 0, /* tp_hash */
815 818 0, /* tp_call */
816 819 0, /* tp_str */
817 820 0, /* tp_getattro */
818 821 0, /* tp_setattro */
819 822 0, /* tp_as_buffer */
820 823 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_SEQUENCE_IN, /* tp_flags */
821 824 "TODO(augie)", /* tp_doc */
822 825 0, /* tp_traverse */
823 826 0, /* tp_clear */
824 827 0, /* tp_richcompare */
825 828 0, /* tp_weaklistoffset */
826 829 (getiterfunc)lazymanifest_getiter, /* tp_iter */
827 830 0, /* tp_iternext */
828 831 lazymanifest_methods, /* tp_methods */
829 832 0, /* tp_members */
830 833 0, /* tp_getset */
831 834 0, /* tp_base */
832 835 0, /* tp_dict */
833 836 0, /* tp_descr_get */
834 837 0, /* tp_descr_set */
835 838 0, /* tp_dictoffset */
836 839 (initproc)lazymanifest_init, /* tp_init */
837 840 0, /* tp_alloc */
838 841 };
839 842
840 843 void manifest_module_init(PyObject * mod)
841 844 {
842 845 lazymanifestType.tp_new = PyType_GenericNew;
843 846 if (PyType_Ready(&lazymanifestType) < 0)
844 847 return;
845 848 Py_INCREF(&lazymanifestType);
846 849
847 850 PyModule_AddObject(mod, "lazymanifest",
848 851 (PyObject *)&lazymanifestType);
849 852 }
General Comments 0
You need to be logged in to leave comments. Login now