##// END OF EJS Templates
copies: no longer cache the ChangedFiles during copy tracing...
copies: no longer cache the ChangedFiles during copy tracing Now that the copies information for both parents are processed all at once, we no longer needs to cache this information, so we simplify the code. The simpler code is also a (tiny) bit faster overall. Repo Case Source-Rev Dest-Rev # of revisions old time new time Difference Factor time per rev --------------------------------------------------------------------------------------------------------------------------------------------------------------- mercurial x_revs_x_added_0_copies ad6b123de1c7 39cfcef4f463 : 1 revs, 0.000041 s, 0.000041 s, +0.000000 s, × 1.0000, 41 µs/rev mercurial x_revs_x_added_x_copies 2b1c78674230 0c1d10351869 : 6 revs, 0.000102 s, 0.000096 s, -0.000006 s, × 0.9412, 16 µs/rev mercurial x000_revs_x000_added_x_copies 81f8ff2a9bf2 dd3267698d84 : 1032 revs, 0.004254 s, 0.004039 s, -0.000215 s, × 0.9495, 3 µs/rev pypy x_revs_x_added_0_copies aed021ee8ae8 099ed31b181b : 9 revs, 0.000282 s, 0.000189 s, -0.000093 s, × 0.6702, 21 µs/rev pypy x_revs_x000_added_0_copies 4aa4e1f8e19a 359343b9ac0e : 1 revs, 0.000048 s, 0.000047 s, -0.000001 s, × 0.9792, 47 µs/rev pypy x_revs_x_added_x_copies ac52eb7bbbb0 72e022663155 : 7 revs, 0.000211 s, 0.000103 s, -0.000108 s, × 0.4882, 14 µs/rev pypy x_revs_x00_added_x_copies c3b14617fbd7 ace7255d9a26 : 1 revs, 0.000375 s, 0.000286 s, -0.000089 s, × 0.7627, 286 µs/rev pypy x_revs_x000_added_x000_copies df6f7a526b60 a83dc6a2d56f : 6 revs, 0.010574 s, 0.010436 s, -0.000138 s, × 0.9869, 1739 µs/rev pypy x000_revs_xx00_added_0_copies 89a76aede314 2f22446ff07e : 4785 revs, 0.049974 s, 0.047465 s, -0.002509 s, × 0.9498, 9 µs/rev pypy x000_revs_x000_added_x_copies 8a3b5bfd266e 2c68e87c3efe : 6780 revs, 0.084300 s, 0.082351 s, -0.001949 s, × 0.9769, 12 µs/rev pypy x000_revs_x000_added_x000_copies 89a76aede314 7b3dda341c84 : 5441 revs, 0.060128 s, 0.058757 s, -0.001371 s, × 0.9772, 10 µs/rev pypy x0000_revs_x_added_0_copies d1defd0dc478 c9cb1334cc78 : 43645 revs, 0.686542 s, 0.674129 s, -0.012413 s, × 0.9819, 15 µs/rev pypy x0000_revs_xx000_added_0_copies bf2c629d0071 4ffed77c095c : 2 revs, 0.009277 s, 0.009434 s, +0.000157 s, × 1.0169, 4717 µs/rev pypy x0000_revs_xx000_added_x000_copies 08ea3258278e d9fa043f30c0 : 11316 revs, 0.114733 s, 0.111935 s, -0.002798 s, × 0.9756, 9 µs/rev netbeans x_revs_x_added_0_copies fb0955ffcbcd a01e9239f9e7 : 2 revs, 0.000081 s, 0.000078 s, -0.000003 s, × 0.9630, 39 µs/rev netbeans x_revs_x000_added_0_copies 6f360122949f 20eb231cc7d0 : 2 revs, 0.000107 s, 0.000106 s, -0.000001 s, × 0.9907, 53 µs/rev netbeans x_revs_x_added_x_copies 1ada3faf6fb6 5a39d12eecf4 : 3 revs, 0.000173 s, 0.000162 s, -0.000011 s, × 0.9364, 54 µs/rev netbeans x_revs_x00_added_x_copies 35be93ba1e2c 9eec5e90c05f : 9 revs, 0.000698 s, 0.000695 s, -0.000003 s, × 0.9957, 77 µs/rev netbeans x000_revs_xx00_added_0_copies eac3045b4fdd 51d4ae7f1290 : 1421 revs, 0.009248 s, 0.008901 s, -0.000347 s, × 0.9625, 6 µs/rev netbeans x000_revs_x000_added_x_copies e2063d266acd 6081d72689dc : 1533 revs, 0.015446 s, 0.014333 s, -0.001113 s, × 0.9279, 9 µs/rev netbeans x000_revs_x000_added_x000_copies ff453e9fee32 411350406ec2 : 5750 revs, 0.074373 s, 0.071998 s, -0.002375 s, × 0.9681, 12 µs/rev netbeans x0000_revs_xx000_added_x000_copies 588c2d1ced70 1aad62e59ddd : 66949 revs, 0.639870 s, 0.615346 s, -0.024524 s, × 0.9617, 9 µs/rev mozilla-central x_revs_x_added_0_copies 3697f962bb7b 7015fcdd43a2 : 2 revs, 0.000088 s, 0.000085 s, -0.000003 s, × 0.9659, 42 µs/rev mozilla-central x_revs_x000_added_0_copies dd390860c6c9 40d0c5bed75d : 8 revs, 0.000199 s, 0.000199 s, +0.000000 s, × 1.0000, 24 µs/rev mozilla-central x_revs_x_added_x_copies 8d198483ae3b 14207ffc2b2f : 9 revs, 0.000171 s, 0.000169 s, -0.000002 s, × 0.9883, 18 µs/rev mozilla-central x_revs_x00_added_x_copies 98cbc58cc6bc 446a150332c3 : 7 revs, 0.000592 s, 0.000590 s, -0.000002 s, × 0.9966, 84 µs/rev mozilla-central x_revs_x000_added_x000_copies 3c684b4b8f68 0a5e72d1b479 : 3 revs, 0.003151 s, 0.003122 s, -0.000029 s, × 0.9908, 1040 µs/rev mozilla-central x_revs_x0000_added_x0000_copies effb563bb7e5 c07a39dc4e80 : 6 revs, 0.061612 s, 0.061192 s, -0.000420 s, × 0.9932, 10198 µs/rev mozilla-central x000_revs_xx00_added_0_copies 6100d773079a 04a55431795e : 1593 revs, 0.005381 s, 0.005137 s, -0.000244 s, × 0.9547, 3 µs/rev mozilla-central x000_revs_x000_added_x_copies 9f17a6fc04f9 2d37b966abed : 41 revs, 0.003742 s, 0.003585 s, -0.000157 s, × 0.9580, 87 µs/rev mozilla-central x000_revs_x000_added_x000_copies 7c97034feb78 4407bd0c6330 : 7839 revs, 0.061983 s, 0.060592 s, -0.001391 s, × 0.9776, 7 µs/rev mozilla-central x0000_revs_xx000_added_0_copies 9eec5917337d 67118cc6dcad : 615 revs, 0.019861 s, 0.019596 s, -0.000265 s, × 0.9867, 31 µs/rev mozilla-central x0000_revs_xx000_added_x000_copies f78c615a656c 96a38b690156 : 30263 revs, 0.188101 s, 0.183558 s, -0.004543 s, × 0.9758, 6 µs/rev mozilla-central x00000_revs_x0000_added_x0000_copies 6832ae71433c 4c222a1d9a00 : 153721 revs, 1.806696 s, 1.758083 s, -0.048613 s, × 0.9731, 11 µs/rev mozilla-central x00000_revs_x00000_added_x000_copies 76caed42cf7c 1daa622bbe42 : 204976 revs, 2.682987 s, 2.592955 s, -0.090032 s, × 0.9664, 12 µs/rev mozilla-try x_revs_x_added_0_copies aaf6dde0deb8 9790f499805a : 2 revs, 0.000852 s, 0.000844 s, -0.000008 s, × 0.9906, 422 µs/rev mozilla-try x_revs_x000_added_0_copies d8d0222927b4 5bb8ce8c7450 : 2 revs, 0.000859 s, 0.000861 s, +0.000002 s, × 1.0023, 430 µs/rev mozilla-try x_revs_x_added_x_copies 092fcca11bdb 936255a0384a : 4 revs, 0.000150 s, 0.000150 s, +0.000000 s, × 1.0000, 37 µs/rev mozilla-try x_revs_x00_added_x_copies b53d2fadbdb5 017afae788ec : 2 revs, 0.001158 s, 0.001166 s, +0.000008 s, × 1.0069, 583 µs/rev mozilla-try x_revs_x000_added_x000_copies 20408ad61ce5 6f0ee96e21ad : 1 revs, 0.027240 s, 0.027359 s, +0.000119 s, × 1.0044, 27359 µs/rev mozilla-try x_revs_x0000_added_x0000_copies effb563bb7e5 c07a39dc4e80 : 6 revs, 0.062824 s, 0.061848 s, -0.000976 s, × 0.9845, 10308 µs/rev mozilla-try x000_revs_xx00_added_0_copies 6100d773079a 04a55431795e : 1593 revs, 0.005463 s, 0.005110 s, -0.000353 s, × 0.9354, 3 µs/rev mozilla-try x000_revs_x000_added_x_copies 9f17a6fc04f9 2d37b966abed : 41 revs, 0.004238 s, 0.004168 s, -0.000070 s, × 0.9835, 101 µs/rev mozilla-try x000_revs_x000_added_x000_copies 1346fd0130e4 4c65cbdabc1f : 6657 revs, 0.064113 s, 0.063414 s, -0.000699 s, × 0.9891, 9 µs/rev mozilla-try x0000_revs_x_added_0_copies 63519bfd42ee a36a2a865d92 : 40314 revs, 0.294063 s, 0.288301 s, -0.005762 s, × 0.9804, 7 µs/rev mozilla-try x0000_revs_x_added_x_copies 9fe69ff0762d bcabf2a78927 : 38690 revs, 0.281493 s, 0.275798 s, -0.005695 s, × 0.9798, 7 µs/rev mozilla-try x0000_revs_xx000_added_x_copies 156f6e2674f2 4d0f2c178e66 : 8598 revs, 0.076323 s, 0.074640 s, -0.001683 s, × 0.9779, 8 µs/rev mozilla-try x0000_revs_xx000_added_0_copies 9eec5917337d 67118cc6dcad : 615 revs, 0.020390 s, 0.020327 s, -0.000063 s, × 0.9969, 33 µs/rev mozilla-try x0000_revs_xx000_added_x000_copies 89294cd501d9 7ccb2fc7ccb5 : 97052 revs, 3.023879 s, 2.970385 s, -0.053494 s, × 0.9823, 30 µs/rev mozilla-try x0000_revs_x0000_added_x0000_copies e928c65095ed e951f4ad123a : 52031 revs, 0.735549 s, 0.719432 s, -0.016117 s, × 0.9781, 13 µs/rev mozilla-try x00000_revs_x_added_0_copies 6a320851d377 1ebb79acd503 : 363753 revs, 18.568900 s, 18.165143 s, -0.403757 s, × 0.9783, 49 µs/rev mozilla-try x00000_revs_x00000_added_0_copies dc8a3ca7010e d16fde900c9c : 34414 revs, 0.502584 s, 0.486769 s, -0.015815 s, × 0.9685, 14 µs/rev mozilla-try x00000_revs_x_added_x_copies 5173c4b6f97c 95d83ee7242d : 362229 revs, 18.356645 s, 17.913924 s, -0.442721 s, × 0.9759, 49 µs/rev mozilla-try x00000_revs_x000_added_x_copies 9126823d0e9c ca82787bb23c : 359344 revs, 18.250393 s, 17.660113 s, -0.590280 s, × 0.9677, 49 µs/rev mozilla-try x00000_revs_x0000_added_x0000_copies 8d3fafa80d4b eb884023b810 : 192665 revs, 2.792459 s, 2.709446 s, -0.083013 s, × 0.9703, 14 µs/rev mozilla-try x00000_revs_x00000_added_x0000_copies 1b661134e2ca 1ae03d022d6d : 228985 revs, 107.697264 s, 107.796891 s, +0.099627 s, × 1.0009, 470 µs/rev mozilla-try x00000_revs_x00000_added_x000_copies 9b2a99adc05e 8e29777b48e6 : 382065 revs, 63.961040 s, 63.575217 s, -0.385823 s, × 0.9940, 166 µs/rev Differential Revision: https://phab.mercurial-scm.org/D9423

File last commit:

r45701:9719e118 default
r46765:a132aa59 default
Show More
manifest.c
1011 lines | 25.9 KiB | text/x-c | CLexer
Yuya Nishihara
parsers: switch to policy importer...
r32372 /*
* manifest.c - manifest type that does on-demand parsing.
*
* Copyright 2015, Google Inc.
*
* This software may be used and distributed according to the terms of
* the GNU General Public License, incorporated herein by reference.
*/
#include <Python.h>
#include <assert.h>
Gregory Szorc
cext: reorder #include...
r34439 #include <stdlib.h>
Yuya Nishihara
parsers: switch to policy importer...
r32372 #include <string.h>
Yuya Nishihara
cext: factor out header for charencode.c...
r33753 #include "charencode.h"
Yuya Nishihara
parsers: switch to policy importer...
r32372 #include "util.h"
#define DEFAULT_LINES 100000
typedef struct {
char *start;
Py_ssize_t len; /* length of line including terminal newline */
char hash_suffix;
bool from_malloc;
bool deleted;
} line;
typedef struct {
PyObject_HEAD
PyObject *pydata;
line *lines;
int numlines; /* number of line entries */
int livelines; /* number of non-deleted lines */
int maxlines; /* allocated number of lines */
bool dirty;
} lazymanifest;
#define MANIFEST_OOM -1
#define MANIFEST_NOT_SORTED -2
#define MANIFEST_MALFORMED -3
Augie Fackler
manifest: make sure there's a filename before bothering to look for newline...
r40635 #define MANIFEST_BOGUS_FILENAME -4
Augie Fackler
manifest: also reject obviously-too-short lines when parsing lines...
r40636 #define MANIFEST_TOO_SHORT_LINE -5
Yuya Nishihara
parsers: switch to policy importer...
r32372
/* get the length of the path for a line */
Kyle Lippincott
cext: fix compiler warning about sign changing...
r44559 static Py_ssize_t pathlen(line *l)
Gregory Szorc
cext: wrap before brace for functions...
r34441 {
Yuya Nishihara
manifest: rewrite pathlen() to not cross entry boundary...
r39975 const char *end = memchr(l->start, '\0', l->len);
Kyle Lippincott
cext: fix compiler warning about sign changing...
r44559 return (end) ? (Py_ssize_t)(end - l->start) : l->len;
Yuya Nishihara
parsers: switch to policy importer...
r32372 }
/* get the node value of a single line */
Joerg Sonnenberger
manifest: tigher manifest parsing and flag use...
r45678 static PyObject *nodeof(line *l, char *flag)
Gregory Szorc
cext: wrap before brace for functions...
r34441 {
Yuya Nishihara
parsers: switch to policy importer...
r32372 char *s = l->start;
Kyle Lippincott
cext: fix compiler warning about sign changing...
r44559 Py_ssize_t llen = pathlen(l);
Augie Fackler
manifest: start removing 40-byte hash restrictions from C code...
r45192 Py_ssize_t hlen = l->len - llen - 2;
Joerg Sonnenberger
manifest: tigher manifest parsing and flag use...
r45678 Py_ssize_t hlen_raw;
Yuya Nishihara
manifest: fix out-of-bounds read of corrupted manifest entry...
r39976 PyObject *hash;
if (llen + 1 + 40 + 1 > l->len) { /* path '\0' hash '\n' */
PyErr_SetString(PyExc_ValueError, "manifest line too short");
return NULL;
}
Joerg Sonnenberger
manifest: tigher manifest parsing and flag use...
r45678 /* Detect flags after the hash first. */
switch (s[llen + hlen]) {
case 'l':
case 't':
case 'x':
*flag = s[llen + hlen];
--hlen;
break;
default:
*flag = '\0';
break;
}
Augie Fackler
manifest: start removing 40-byte hash restrictions from C code...
r45192 switch (hlen) {
case 40: /* sha1 */
Joerg Sonnenberger
manifest: tigher manifest parsing and flag use...
r45678 hlen_raw = 20;
Augie Fackler
manifest: start removing 40-byte hash restrictions from C code...
r45192 break;
case 64: /* new hash */
hlen_raw = 32;
break;
default:
PyErr_SetString(PyExc_ValueError, "invalid node length in manifest");
return NULL;
}
hash = unhexlify(s + llen + 1, hlen_raw * 2);
Yuya Nishihara
parsers: switch to policy importer...
r32372 if (!hash) {
return NULL;
}
if (l->hash_suffix != '\0') {
Augie Fackler
manifest: start removing 40-byte hash restrictions from C code...
r45192 char newhash[33];
memcpy(newhash, PyBytes_AsString(hash), hlen_raw);
Yuya Nishihara
parsers: switch to policy importer...
r32372 Py_DECREF(hash);
Augie Fackler
manifest: start removing 40-byte hash restrictions from C code...
r45192 newhash[hlen_raw] = l->hash_suffix;
hash = PyBytes_FromStringAndSize(newhash, hlen_raw+1);
Yuya Nishihara
parsers: switch to policy importer...
r32372 }
return hash;
}
/* get the node hash and flags of a line as a tuple */
static PyObject *hashflags(line *l)
{
Joerg Sonnenberger
manifest: tigher manifest parsing and flag use...
r45678 char flag;
PyObject *hash = nodeof(l, &flag);
Yuya Nishihara
parsers: switch to policy importer...
r32372 PyObject *flags;
PyObject *tup;
if (!hash)
return NULL;
Joerg Sonnenberger
manifest: tigher manifest parsing and flag use...
r45678 flags = PyBytes_FromStringAndSize(&flag, flag ? 1 : 0);
Yuya Nishihara
parsers: switch to policy importer...
r32372 if (!flags) {
Py_DECREF(hash);
return NULL;
}
tup = PyTuple_Pack(2, hash, flags);
Py_DECREF(flags);
Py_DECREF(hash);
return tup;
}
/* if we're about to run out of space in the line index, add more */
static bool realloc_if_full(lazymanifest *self)
{
if (self->numlines == self->maxlines) {
self->maxlines *= 2;
self->lines = realloc(self->lines, self->maxlines * sizeof(line));
}
return !!self->lines;
}
/*
* Find the line boundaries in the manifest that 'data' points to and store
* information about each line in 'self'.
*/
static int find_lines(lazymanifest *self, char *data, Py_ssize_t len)
{
char *prev = NULL;
while (len > 0) {
line *l;
Augie Fackler
manifest: make sure there's a filename before bothering to look for newline...
r40635 char *next;
if (*data == '\0') {
/* It's implausible there's no filename, don't
* even bother looking for the newline. */
return MANIFEST_BOGUS_FILENAME;
}
next = memchr(data, '\n', len);
Yuya Nishihara
parsers: switch to policy importer...
r32372 if (!next) {
return MANIFEST_MALFORMED;
}
Augie Fackler
manifest: reject lines shorter than 42 bytes, not 22...
r40801 if ((next - data) < 42) {
/* We should have at least 42 bytes in a line:
Augie Fackler
manifest: also reject obviously-too-short lines when parsing lines...
r40636 1 byte filename
1 NUL
Augie Fackler
manifest: reject lines shorter than 42 bytes, not 22...
r40801 40 bytes of hash
Augie Fackler
manifest: also reject obviously-too-short lines when parsing lines...
r40636 so we can give up here.
*/
return MANIFEST_TOO_SHORT_LINE;
}
Yuya Nishihara
parsers: switch to policy importer...
r32372 next++; /* advance past newline */
if (prev && strcmp(prev, data) > -1) {
/* This data isn't sorted, so we have to abort. */
return MANIFEST_NOT_SORTED;
}
Augie Fackler
manifest: perform cheap checks before potentially allocating memory...
r40637 if (!realloc_if_full(self)) {
return MANIFEST_OOM; /* no memory */
}
Yuya Nishihara
parsers: switch to policy importer...
r32372 l = self->lines + ((self->numlines)++);
l->start = data;
l->len = next - data;
l->hash_suffix = '\0';
l->from_malloc = false;
l->deleted = false;
len = len - l->len;
prev = data;
data = next;
}
self->livelines = self->numlines;
return 0;
}
Yuya Nishihara
manifest: fix possible SEGV caused by uninitialized lazymanifest fields...
r38346 static void lazymanifest_init_early(lazymanifest *self)
{
self->pydata = NULL;
self->lines = NULL;
self->numlines = 0;
self->maxlines = 0;
}
Yuya Nishihara
parsers: switch to policy importer...
r32372 static int lazymanifest_init(lazymanifest *self, PyObject *args)
{
char *data;
Py_ssize_t len;
int err, ret;
PyObject *pydata;
Yuya Nishihara
manifest: fix possible SEGV caused by uninitialized lazymanifest fields...
r38346
lazymanifest_init_early(self);
Yuya Nishihara
parsers: switch to policy importer...
r32372 if (!PyArg_ParseTuple(args, "S", &pydata)) {
return -1;
}
err = PyBytes_AsStringAndSize(pydata, &data, &len);
self->dirty = false;
if (err == -1)
return -1;
self->pydata = pydata;
Py_INCREF(self->pydata);
Py_BEGIN_ALLOW_THREADS
self->lines = malloc(DEFAULT_LINES * sizeof(line));
self->maxlines = DEFAULT_LINES;
self->numlines = 0;
if (!self->lines)
ret = MANIFEST_OOM;
else
ret = find_lines(self, data, len);
Py_END_ALLOW_THREADS
switch (ret) {
case 0:
break;
case MANIFEST_OOM:
PyErr_NoMemory();
break;
case MANIFEST_NOT_SORTED:
PyErr_Format(PyExc_ValueError,
"Manifest lines not in sorted order.");
break;
case MANIFEST_MALFORMED:
PyErr_Format(PyExc_ValueError,
"Manifest did not end in a newline.");
break;
Augie Fackler
manifest: make sure there's a filename before bothering to look for newline...
r40635 case MANIFEST_BOGUS_FILENAME:
PyErr_Format(
PyExc_ValueError,
"Manifest had an entry with a zero-length filename.");
break;
Augie Fackler
manifest: also reject obviously-too-short lines when parsing lines...
r40636 case MANIFEST_TOO_SHORT_LINE:
PyErr_Format(
PyExc_ValueError,
"Manifest had implausibly-short line.");
break;
Yuya Nishihara
parsers: switch to policy importer...
r32372 default:
PyErr_Format(PyExc_ValueError,
"Unknown problem parsing manifest.");
}
return ret == 0 ? 0 : -1;
}
static void lazymanifest_dealloc(lazymanifest *self)
{
/* free any extra lines we had to allocate */
int i;
Josef 'Jeff' Sipek
lazymanifest: don't crash when out of memory (issue5916)...
r38321 for (i = 0; self->lines && (i < self->numlines); i++) {
Yuya Nishihara
parsers: switch to policy importer...
r32372 if (self->lines[i].from_malloc) {
free(self->lines[i].start);
}
}
Josef 'Jeff' Sipek
cext: stop worrying and love the free(NULL)...
r38320 free(self->lines);
self->lines = NULL;
Yuya Nishihara
parsers: switch to policy importer...
r32372 if (self->pydata) {
Py_DECREF(self->pydata);
self->pydata = NULL;
}
PyObject_Del(self);
}
/* iteration support */
typedef struct {
PyObject_HEAD lazymanifest *m;
Py_ssize_t pos;
} lmIter;
static void lmiter_dealloc(PyObject *o)
{
lmIter *self = (lmIter *)o;
Py_DECREF(self->m);
PyObject_Del(self);
}
static line *lmiter_nextline(lmIter *self)
{
do {
self->pos++;
if (self->pos >= self->m->numlines) {
return NULL;
}
/* skip over deleted manifest entries */
} while (self->m->lines[self->pos].deleted);
return self->m->lines + self->pos;
}
static PyObject *lmiter_iterentriesnext(PyObject *o)
{
Kyle Lippincott
cext: fix compiler warning about sign changing...
r44559 Py_ssize_t pl;
Yuya Nishihara
parsers: switch to policy importer...
r32372 line *l;
Joerg Sonnenberger
manifest: tigher manifest parsing and flag use...
r45678 char flag;
Yuya Nishihara
parsers: switch to policy importer...
r32372 PyObject *ret = NULL, *path = NULL, *hash = NULL, *flags = NULL;
l = lmiter_nextline((lmIter *)o);
if (!l) {
goto done;
}
pl = pathlen(l);
path = PyBytes_FromStringAndSize(l->start, pl);
Joerg Sonnenberger
manifest: tigher manifest parsing and flag use...
r45678 hash = nodeof(l, &flag);
Yuya Nishihara
manifest: fix out-of-bounds read of corrupted manifest entry...
r39976 if (!path || !hash) {
goto done;
}
Joerg Sonnenberger
manifest: tigher manifest parsing and flag use...
r45678 flags = PyBytes_FromStringAndSize(&flag, flag ? 1 : 0);
Yuya Nishihara
manifest: fix out-of-bounds read of corrupted manifest entry...
r39976 if (!flags) {
Yuya Nishihara
parsers: switch to policy importer...
r32372 goto done;
}
ret = PyTuple_Pack(3, path, hash, flags);
done:
Py_XDECREF(path);
Py_XDECREF(hash);
Py_XDECREF(flags);
return ret;
}
#ifdef IS_PY3K
#define LAZYMANIFESTENTRIESITERATOR_TPFLAGS Py_TPFLAGS_DEFAULT
#else
#define LAZYMANIFESTENTRIESITERATOR_TPFLAGS Py_TPFLAGS_DEFAULT \
| Py_TPFLAGS_HAVE_ITER
#endif
static PyTypeObject lazymanifestEntriesIterator = {
Augie Fackler
cext: add /* header */ comment to all PyVarObject_HEAD_INIT() calls...
r34862 PyVarObject_HEAD_INIT(NULL, 0) /* header */
Yuya Nishihara
parsers: switch to policy importer...
r32372 "parsers.lazymanifest.entriesiterator", /*tp_name */
sizeof(lmIter), /*tp_basicsize */
0, /*tp_itemsize */
lmiter_dealloc, /*tp_dealloc */
0, /*tp_print */
0, /*tp_getattr */
0, /*tp_setattr */
0, /*tp_compare */
0, /*tp_repr */
0, /*tp_as_number */
0, /*tp_as_sequence */
0, /*tp_as_mapping */
0, /*tp_hash */
0, /*tp_call */
0, /*tp_str */
0, /*tp_getattro */
0, /*tp_setattro */
0, /*tp_as_buffer */
LAZYMANIFESTENTRIESITERATOR_TPFLAGS, /* tp_flags */
"Iterator for 3-tuples in a lazymanifest.", /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
PyObject_SelfIter, /* tp_iter: __iter__() method */
lmiter_iterentriesnext, /* tp_iternext: next() method */
};
static PyObject *lmiter_iterkeysnext(PyObject *o)
{
Kyle Lippincott
cext: fix compiler warning about sign changing...
r44559 Py_ssize_t pl;
Yuya Nishihara
parsers: switch to policy importer...
r32372 line *l = lmiter_nextline((lmIter *)o);
if (!l) {
return NULL;
}
pl = pathlen(l);
return PyBytes_FromStringAndSize(l->start, pl);
}
#ifdef IS_PY3K
#define LAZYMANIFESTKEYSITERATOR_TPFLAGS Py_TPFLAGS_DEFAULT
#else
#define LAZYMANIFESTKEYSITERATOR_TPFLAGS Py_TPFLAGS_DEFAULT \
| Py_TPFLAGS_HAVE_ITER
#endif
static PyTypeObject lazymanifestKeysIterator = {
Augie Fackler
cext: add /* header */ comment to all PyVarObject_HEAD_INIT() calls...
r34862 PyVarObject_HEAD_INIT(NULL, 0) /* header */
Yuya Nishihara
parsers: switch to policy importer...
r32372 "parsers.lazymanifest.keysiterator", /*tp_name */
sizeof(lmIter), /*tp_basicsize */
0, /*tp_itemsize */
lmiter_dealloc, /*tp_dealloc */
0, /*tp_print */
0, /*tp_getattr */
0, /*tp_setattr */
0, /*tp_compare */
0, /*tp_repr */
0, /*tp_as_number */
0, /*tp_as_sequence */
0, /*tp_as_mapping */
0, /*tp_hash */
0, /*tp_call */
0, /*tp_str */
0, /*tp_getattro */
0, /*tp_setattro */
0, /*tp_as_buffer */
LAZYMANIFESTKEYSITERATOR_TPFLAGS, /* tp_flags */
"Keys iterator for a lazymanifest.", /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
PyObject_SelfIter, /* tp_iter: __iter__() method */
lmiter_iterkeysnext, /* tp_iternext: next() method */
};
static lazymanifest *lazymanifest_copy(lazymanifest *self);
static PyObject *lazymanifest_getentriesiter(lazymanifest *self)
{
lmIter *i = NULL;
lazymanifest *t = lazymanifest_copy(self);
if (!t) {
PyErr_NoMemory();
return NULL;
}
i = PyObject_New(lmIter, &lazymanifestEntriesIterator);
if (i) {
i->m = t;
i->pos = -1;
} else {
Py_DECREF(t);
PyErr_NoMemory();
}
return (PyObject *)i;
}
static PyObject *lazymanifest_getkeysiter(lazymanifest *self)
{
lmIter *i = NULL;
lazymanifest *t = lazymanifest_copy(self);
if (!t) {
PyErr_NoMemory();
return NULL;
}
i = PyObject_New(lmIter, &lazymanifestKeysIterator);
if (i) {
i->m = t;
i->pos = -1;
} else {
Py_DECREF(t);
PyErr_NoMemory();
}
return (PyObject *)i;
}
/* __getitem__ and __setitem__ support */
static Py_ssize_t lazymanifest_size(lazymanifest *self)
{
return self->livelines;
}
static int linecmp(const void *left, const void *right)
{
return strcmp(((const line *)left)->start,
((const line *)right)->start);
}
static PyObject *lazymanifest_getitem(lazymanifest *self, PyObject *key)
{
line needle;
line *hit;
if (!PyBytes_Check(key)) {
PyErr_Format(PyExc_TypeError,
"getitem: manifest keys must be a string.");
return NULL;
}
needle.start = PyBytes_AsString(key);
hit = bsearch(&needle, self->lines, self->numlines, sizeof(line),
&linecmp);
if (!hit || hit->deleted) {
PyErr_Format(PyExc_KeyError, "No such manifest entry.");
return NULL;
}
return hashflags(hit);
}
static int lazymanifest_delitem(lazymanifest *self, PyObject *key)
{
line needle;
line *hit;
if (!PyBytes_Check(key)) {
PyErr_Format(PyExc_TypeError,
"delitem: manifest keys must be a string.");
return -1;
}
needle.start = PyBytes_AsString(key);
hit = bsearch(&needle, self->lines, self->numlines, sizeof(line),
&linecmp);
if (!hit || hit->deleted) {
PyErr_Format(PyExc_KeyError,
"Tried to delete nonexistent manifest entry.");
return -1;
}
self->dirty = true;
hit->deleted = true;
self->livelines--;
return 0;
}
/* Do a binary search for the insertion point for new, creating the
* new entry if needed. */
Gregory Szorc
cext: wrap before brace for functions...
r34441 static int internalsetitem(lazymanifest *self, line *new)
{
Yuya Nishihara
parsers: switch to policy importer...
r32372 int start = 0, end = self->numlines;
while (start < end) {
int pos = start + (end - start) / 2;
int c = linecmp(new, self->lines + pos);
if (c < 0)
end = pos;
else if (c > 0)
start = pos + 1;
else {
if (self->lines[pos].deleted)
self->livelines++;
if (self->lines[pos].from_malloc)
free(self->lines[pos].start);
start = pos;
goto finish;
}
}
/* being here means we need to do an insert */
if (!realloc_if_full(self)) {
PyErr_NoMemory();
return -1;
}
memmove(self->lines + start + 1, self->lines + start,
(self->numlines - start) * sizeof(line));
self->numlines++;
self->livelines++;
finish:
self->lines[start] = *new;
self->dirty = true;
return 0;
}
static int lazymanifest_setitem(
lazymanifest *self, PyObject *key, PyObject *value)
{
char *path;
Py_ssize_t plen;
PyObject *pyhash;
Py_ssize_t hlen;
char *hash;
PyObject *pyflags;
char *flags;
Py_ssize_t flen;
Kyle Lippincott
cext: change two more vars to Py_ssize_t in manifest.c...
r44594 Py_ssize_t dlen;
Yuya Nishihara
parsers: switch to policy importer...
r32372 char *dest;
int i;
line new;
if (!PyBytes_Check(key)) {
PyErr_Format(PyExc_TypeError,
"setitem: manifest keys must be a string.");
return -1;
}
if (!value) {
return lazymanifest_delitem(self, key);
}
if (!PyTuple_Check(value) || PyTuple_Size(value) != 2) {
PyErr_Format(PyExc_TypeError,
"Manifest values must be a tuple of (node, flags).");
return -1;
}
if (PyBytes_AsStringAndSize(key, &path, &plen) == -1) {
return -1;
}
pyhash = PyTuple_GetItem(value, 0);
if (!PyBytes_Check(pyhash)) {
PyErr_Format(PyExc_TypeError,
Joerg Sonnenberger
manifest: tigher manifest parsing and flag use...
r45678 "node must be a 20 or 32 bytes string");
Yuya Nishihara
parsers: switch to policy importer...
r32372 return -1;
}
hlen = PyBytes_Size(pyhash);
Joerg Sonnenberger
manifest: tigher manifest parsing and flag use...
r45678 if (hlen != 20 && hlen != 32) {
Yuya Nishihara
parsers: switch to policy importer...
r32372 PyErr_Format(PyExc_TypeError,
Joerg Sonnenberger
manifest: tigher manifest parsing and flag use...
r45678 "node must be a 20 or 32 bytes string");
Yuya Nishihara
parsers: switch to policy importer...
r32372 return -1;
}
hash = PyBytes_AsString(pyhash);
pyflags = PyTuple_GetItem(value, 1);
if (!PyBytes_Check(pyflags) || PyBytes_Size(pyflags) > 1) {
PyErr_Format(PyExc_TypeError,
Joerg Sonnenberger
manifest: tigher manifest parsing and flag use...
r45678 "flags must a 0 or 1 bytes string");
Yuya Nishihara
parsers: switch to policy importer...
r32372 return -1;
}
if (PyBytes_AsStringAndSize(pyflags, &flags, &flen) == -1) {
return -1;
}
Joerg Sonnenberger
manifest: tigher manifest parsing and flag use...
r45678 if (flen == 1) {
switch (*flags) {
case 'l':
case 't':
case 'x':
break;
default:
PyErr_Format(PyExc_TypeError, "invalid manifest flag");
return -1;
}
}
Yuya Nishihara
parsers: switch to policy importer...
r32372 /* one null byte and one newline */
Joerg Sonnenberger
manifest: tigher manifest parsing and flag use...
r45678 dlen = plen + hlen * 2 + 1 + flen + 1;
Yuya Nishihara
parsers: switch to policy importer...
r32372 dest = malloc(dlen);
if (!dest) {
PyErr_NoMemory();
return -1;
}
memcpy(dest, path, plen + 1);
Joerg Sonnenberger
manifest: tigher manifest parsing and flag use...
r45678 for (i = 0; i < hlen; i++) {
Yuya Nishihara
parsers: switch to policy importer...
r32372 /* Cast to unsigned, so it will not get sign-extended when promoted
* to int (as is done when passing to a variadic function)
*/
sprintf(dest + plen + 1 + (i * 2), "%02x", (unsigned char)hash[i]);
}
Joerg Sonnenberger
manifest: tigher manifest parsing and flag use...
r45678 memcpy(dest + plen + 2 * hlen + 1, flags, flen);
dest[plen + 2 * hlen + 1 + flen] = '\n';
Yuya Nishihara
parsers: switch to policy importer...
r32372 new.start = dest;
new.len = dlen;
new.hash_suffix = '\0';
if (hlen > 20) {
new.hash_suffix = hash[20];
}
new.from_malloc = true; /* is `start` a pointer we allocated? */
new.deleted = false; /* is this entry deleted? */
if (internalsetitem(self, &new)) {
return -1;
}
return 0;
}
static PyMappingMethods lazymanifest_mapping_methods = {
(lenfunc)lazymanifest_size, /* mp_length */
(binaryfunc)lazymanifest_getitem, /* mp_subscript */
(objobjargproc)lazymanifest_setitem, /* mp_ass_subscript */
};
/* sequence methods (important or __contains__ builds an iterator) */
static int lazymanifest_contains(lazymanifest *self, PyObject *key)
{
line needle;
line *hit;
if (!PyBytes_Check(key)) {
/* Our keys are always strings, so if the contains
* check is for a non-string, just return false. */
return 0;
}
needle.start = PyBytes_AsString(key);
hit = bsearch(&needle, self->lines, self->numlines, sizeof(line),
&linecmp);
if (!hit || hit->deleted) {
return 0;
}
return 1;
}
static PySequenceMethods lazymanifest_seq_meths = {
(lenfunc)lazymanifest_size, /* sq_length */
0, /* sq_concat */
0, /* sq_repeat */
0, /* sq_item */
0, /* sq_slice */
0, /* sq_ass_item */
0, /* sq_ass_slice */
(objobjproc)lazymanifest_contains, /* sq_contains */
0, /* sq_inplace_concat */
0, /* sq_inplace_repeat */
};
/* Other methods (copy, diff, etc) */
static PyTypeObject lazymanifestType;
/* If the manifest has changes, build the new manifest text and reindex it. */
Gregory Szorc
cext: wrap before brace for functions...
r34441 static int compact(lazymanifest *self)
{
Yuya Nishihara
parsers: switch to policy importer...
r32372 int i;
ssize_t need = 0;
char *data;
line *src, *dst;
PyObject *pydata;
if (!self->dirty)
return 0;
for (i = 0; i < self->numlines; i++) {
if (!self->lines[i].deleted) {
need += self->lines[i].len;
}
}
pydata = PyBytes_FromStringAndSize(NULL, need);
if (!pydata)
return -1;
data = PyBytes_AsString(pydata);
if (!data) {
return -1;
}
src = self->lines;
dst = self->lines;
for (i = 0; i < self->numlines; i++, src++) {
char *tofree = NULL;
if (src->from_malloc) {
tofree = src->start;
}
if (!src->deleted) {
memcpy(data, src->start, src->len);
*dst = *src;
dst->start = data;
dst->from_malloc = false;
data += dst->len;
dst++;
}
free(tofree);
}
Py_DECREF(self->pydata);
self->pydata = pydata;
self->numlines = self->livelines;
self->dirty = false;
return 0;
}
static PyObject *lazymanifest_text(lazymanifest *self)
{
if (compact(self) != 0) {
PyErr_NoMemory();
return NULL;
}
Py_INCREF(self->pydata);
return self->pydata;
}
static lazymanifest *lazymanifest_copy(lazymanifest *self)
{
lazymanifest *copy = NULL;
if (compact(self) != 0) {
goto nomem;
}
copy = PyObject_New(lazymanifest, &lazymanifestType);
if (!copy) {
goto nomem;
}
Yuya Nishihara
manifest: fix possible SEGV caused by uninitialized lazymanifest fields...
r38346 lazymanifest_init_early(copy);
Yuya Nishihara
parsers: switch to policy importer...
r32372 copy->numlines = self->numlines;
copy->livelines = self->livelines;
copy->dirty = false;
copy->lines = malloc(self->maxlines *sizeof(line));
if (!copy->lines) {
goto nomem;
}
memcpy(copy->lines, self->lines, self->numlines * sizeof(line));
copy->maxlines = self->maxlines;
copy->pydata = self->pydata;
Py_INCREF(copy->pydata);
return copy;
nomem:
PyErr_NoMemory();
Py_XDECREF(copy);
return NULL;
}
static lazymanifest *lazymanifest_filtercopy(
lazymanifest *self, PyObject *matchfn)
{
lazymanifest *copy = NULL;
int i;
if (!PyCallable_Check(matchfn)) {
PyErr_SetString(PyExc_TypeError, "matchfn must be callable");
return NULL;
}
/* compact ourselves first to avoid double-frees later when we
* compact tmp so that it doesn't have random pointers to our
* underlying from_malloc-data (self->pydata is safe) */
if (compact(self) != 0) {
goto nomem;
}
copy = PyObject_New(lazymanifest, &lazymanifestType);
if (!copy) {
goto nomem;
}
Yuya Nishihara
manifest: fix possible SEGV caused by uninitialized lazymanifest fields...
r38346 lazymanifest_init_early(copy);
Yuya Nishihara
parsers: switch to policy importer...
r32372 copy->dirty = true;
copy->lines = malloc(self->maxlines * sizeof(line));
if (!copy->lines) {
goto nomem;
}
copy->maxlines = self->maxlines;
copy->numlines = 0;
copy->pydata = self->pydata;
Yuya Nishihara
manifest: incref/decref copy->pydata to clarify 'copy' holds a reference
r39486 Py_INCREF(copy->pydata);
Yuya Nishihara
parsers: switch to policy importer...
r32372 for (i = 0; i < self->numlines; i++) {
PyObject *arglist = NULL, *result = NULL;
Yuya Nishihara
py3: bulk-replace bytes format specifier passed to Py_BuildValue()...
r36637 arglist = Py_BuildValue(PY23("(s)", "(y)"),
self->lines[i].start);
Yuya Nishihara
parsers: switch to policy importer...
r32372 if (!arglist) {
Yuya Nishihara
manifest: fix leak on error return from lazymanifest_filtercopy()...
r39487 goto bail;
Yuya Nishihara
parsers: switch to policy importer...
r32372 }
result = PyObject_CallObject(matchfn, arglist);
Py_DECREF(arglist);
/* if the callback raised an exception, just let it
* through and give up */
if (!result) {
Yuya Nishihara
manifest: fix leak on error return from lazymanifest_filtercopy()...
r39487 goto bail;
Yuya Nishihara
parsers: switch to policy importer...
r32372 }
if (PyObject_IsTrue(result)) {
assert(!(self->lines[i].from_malloc));
copy->lines[copy->numlines++] = self->lines[i];
}
Py_DECREF(result);
}
copy->livelines = copy->numlines;
return copy;
nomem:
PyErr_NoMemory();
Yuya Nishihara
manifest: fix leak on error return from lazymanifest_filtercopy()...
r39487 bail:
Yuya Nishihara
parsers: switch to policy importer...
r32372 Py_XDECREF(copy);
return NULL;
}
static PyObject *lazymanifest_diff(lazymanifest *self, PyObject *args)
{
lazymanifest *other;
PyObject *pyclean = NULL;
bool listclean;
PyObject *emptyTup = NULL, *ret = NULL;
PyObject *es;
int sneedle = 0, oneedle = 0;
if (!PyArg_ParseTuple(args, "O!|O", &lazymanifestType, &other, &pyclean)) {
return NULL;
}
listclean = (!pyclean) ? false : PyObject_IsTrue(pyclean);
es = PyBytes_FromString("");
if (!es) {
goto nomem;
}
emptyTup = PyTuple_Pack(2, Py_None, es);
Py_DECREF(es);
if (!emptyTup) {
goto nomem;
}
ret = PyDict_New();
if (!ret) {
goto nomem;
}
while (sneedle != self->numlines || oneedle != other->numlines) {
line *left = self->lines + sneedle;
line *right = other->lines + oneedle;
int result;
PyObject *key;
PyObject *outer;
/* If we're looking at a deleted entry and it's not
* the end of the manifest, just skip it. */
Jun Wu
lazymanifest: avoid reading uninitialized memory...
r35828 if (sneedle < self->numlines && left->deleted) {
Yuya Nishihara
parsers: switch to policy importer...
r32372 sneedle++;
continue;
}
Jun Wu
lazymanifest: avoid reading uninitialized memory...
r35828 if (oneedle < other->numlines && right->deleted) {
Yuya Nishihara
parsers: switch to policy importer...
r32372 oneedle++;
continue;
}
/* if we're at the end of either manifest, then we
* know the remaining items are adds so we can skip
* the strcmp. */
if (sneedle == self->numlines) {
result = 1;
} else if (oneedle == other->numlines) {
result = -1;
} else {
result = linecmp(left, right);
}
key = result <= 0 ?
PyBytes_FromString(left->start) :
PyBytes_FromString(right->start);
if (!key)
goto nomem;
if (result < 0) {
PyObject *l = hashflags(left);
if (!l) {
goto nomem;
}
outer = PyTuple_Pack(2, l, emptyTup);
Py_DECREF(l);
if (!outer) {
goto nomem;
}
PyDict_SetItem(ret, key, outer);
Py_DECREF(outer);
sneedle++;
} else if (result > 0) {
PyObject *r = hashflags(right);
if (!r) {
goto nomem;
}
outer = PyTuple_Pack(2, emptyTup, r);
Py_DECREF(r);
if (!outer) {
goto nomem;
}
PyDict_SetItem(ret, key, outer);
Py_DECREF(outer);
oneedle++;
} else {
/* file exists in both manifests */
if (left->len != right->len
|| memcmp(left->start, right->start, left->len)
|| left->hash_suffix != right->hash_suffix) {
PyObject *l = hashflags(left);
PyObject *r;
if (!l) {
goto nomem;
}
r = hashflags(right);
if (!r) {
Py_DECREF(l);
goto nomem;
}
outer = PyTuple_Pack(2, l, r);
Py_DECREF(l);
Py_DECREF(r);
if (!outer) {
goto nomem;
}
PyDict_SetItem(ret, key, outer);
Py_DECREF(outer);
} else if (listclean) {
PyDict_SetItem(ret, key, Py_None);
}
sneedle++;
oneedle++;
}
Py_DECREF(key);
}
Py_DECREF(emptyTup);
return ret;
nomem:
PyErr_NoMemory();
Py_XDECREF(ret);
Py_XDECREF(emptyTup);
return NULL;
}
static PyMethodDef lazymanifest_methods[] = {
{"iterkeys", (PyCFunction)lazymanifest_getkeysiter, METH_NOARGS,
"Iterate over file names in this lazymanifest."},
{"iterentries", (PyCFunction)lazymanifest_getentriesiter, METH_NOARGS,
"Iterate over (path, nodeid, flags) tuples in this lazymanifest."},
{"copy", (PyCFunction)lazymanifest_copy, METH_NOARGS,
"Make a copy of this lazymanifest."},
{"filtercopy", (PyCFunction)lazymanifest_filtercopy, METH_O,
"Make a copy of this manifest filtered by matchfn."},
{"diff", (PyCFunction)lazymanifest_diff, METH_VARARGS,
"Compare this lazymanifest to another one."},
{"text", (PyCFunction)lazymanifest_text, METH_NOARGS,
"Encode this manifest to text."},
{NULL},
};
#ifdef IS_PY3K
#define LAZYMANIFEST_TPFLAGS Py_TPFLAGS_DEFAULT
#else
#define LAZYMANIFEST_TPFLAGS Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_SEQUENCE_IN
#endif
static PyTypeObject lazymanifestType = {
Augie Fackler
cext: add /* header */ comment to all PyVarObject_HEAD_INIT() calls...
r34862 PyVarObject_HEAD_INIT(NULL, 0) /* header */
Yuya Nishihara
parsers: switch to policy importer...
r32372 "parsers.lazymanifest", /* tp_name */
sizeof(lazymanifest), /* tp_basicsize */
0, /* tp_itemsize */
(destructor)lazymanifest_dealloc, /* tp_dealloc */
0, /* tp_print */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_compare */
0, /* tp_repr */
0, /* tp_as_number */
&lazymanifest_seq_meths, /* tp_as_sequence */
&lazymanifest_mapping_methods, /* tp_as_mapping */
0, /* tp_hash */
0, /* tp_call */
0, /* tp_str */
0, /* tp_getattro */
0, /* tp_setattro */
0, /* tp_as_buffer */
LAZYMANIFEST_TPFLAGS, /* tp_flags */
"TODO(augie)", /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
(getiterfunc)lazymanifest_getkeysiter, /* tp_iter */
0, /* tp_iternext */
lazymanifest_methods, /* tp_methods */
0, /* tp_members */
0, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
0, /* tp_dictoffset */
(initproc)lazymanifest_init, /* tp_init */
0, /* tp_alloc */
};
void manifest_module_init(PyObject * mod)
{
lazymanifestType.tp_new = PyType_GenericNew;
if (PyType_Ready(&lazymanifestType) < 0)
return;
Py_INCREF(&lazymanifestType);
PyModule_AddObject(mod, "lazymanifest",
(PyObject *)&lazymanifestType);
}