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