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