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