##// END OF EJS Templates
setdiscovery: make progress on most connected groups each roundtrip...
setdiscovery: make progress on most connected groups each roundtrip Consider history like this: o | o | | | o | | | o |/ o | o | | | o | | | o |/ o | o | | | o | | | o |/ o ~ Assume the left mainline is available in the remote repo and the other commits are only in the local repo. Also imagine that instead of 3 local branches with 3 commits on each, there are 1000 branches (the number of commits on each doesn't matter much here). In such a scenario, the current setdiscovery code will pick a sample size of 200 among these branches and ask the remote which of them it has. However, the discovery for each such branch is completely independent of the discovery for the others -- knowing whether the remote has a commit in one branch doesn't give us any information about the other branches. The discovery will therefore take at least 5 roundtrips (maybe more depending on which commit in each linear chain was sampled). Since the discovery for each branch is independent, there is no reason to let one branch wait for another, so this patch makes it so we sample at least as many commits as there are branches. It may still happen (it's very likely, even) that we get multiple samples from one branch and none from another, but that will even out over a few rounds and I think this is still a big improvement. Because of http header size limits, we still use the old behavior unless experimental.httppostargs=true. I've timed this by running `hg debugdiscovery mozilla-unified --debug` in the mozilla-try repo. Both repos were local. Before this patch, last part of the output was: 2249 total queries in 5276.4859s elapsed time: 5276.652634 seconds heads summary: total common heads: 13 also local heads: 4 also remote heads: 8 both: 4 local heads: 28317 common: 4 missing: 28313 remote heads: 12 common: 8 unknown: 4 local changesets: 2014901 common: 530373 missing: 1484528 common heads: 1dad417c28ad 4a108e94d3e2 4d7ef530fffb 5350524bb654 777e60ca8853 7d97fafba271 9cd2ab4d0029 a55ce37217da d38398e5144e dcc6d7a0dc00 e09297892ada e24ec6070d7b fd559328eaf3 After this patch, the output was (including all the samples, since there were so few now): taking initial sample query 2; still undecided: 1599476, sample size is: 108195 sampling from both directions query 3; still undecided: 810922, sample size is: 194158 sampling from both directions query 4; still undecided: 325882, sample size is: 137302 sampling from both directions query 5; still undecided: 111459, sample size is: 74586 sampling from both directions query 6; still undecided: 26805, sample size is: 23960 sampling from both directions query 7; still undecided: 2549, sample size is: 2528 sampling from both directions query 8; still undecided: 21, sample size is: 21 8 total queries in 24.5064s elapsed time: 24.670051 seconds heads summary: total common heads: 13 also local heads: 4 also remote heads: 8 both: 4 local heads: 28317 common: 4 missing: 28313 remote heads: 12 common: 8 unknown: 4 local changesets: 2014901 common: 530373 missing: 1484528 common heads: 1dad417c28ad 4a108e94d3e2 4d7ef530fffb 5350524bb654 777e60ca8853 7d97fafba271 9cd2ab4d0029 a55ce37217da d38398e5144e dcc6d7a0dc00 e09297892ada e24ec6070d7b fd559328eaf3 Differential Revision: https://phab.mercurial-scm.org/D2647

File last commit:

r42530:d8e55c0c default
r42594:5b34972a default
Show More
parsers.c
762 lines | 19.9 KiB | text/x-c | CLexer
Yuya Nishihara
parsers: switch to policy importer...
r32372 /*
parsers.c - efficient content parsing
Copyright 2008 Matt Mackall <mpm@selenic.com> and others
This software may be used and distributed according to the terms of
the GNU General Public License, incorporated herein by reference.
*/
Gregory Szorc
cext: make parsers.c PY_SSIZE_T_CLEAN...
r42235 #define PY_SSIZE_T_CLEAN
Yuya Nishihara
parsers: switch to policy importer...
r32372 #include <Python.h>
#include <ctype.h>
#include <stddef.h>
#include <string.h>
Gregory Szorc
cext: reorder #include...
r34439 #include "bitmanipulation.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"
#ifdef IS_PY3K
/* The mapping of Python types is meant to be temporary to get Python
* 3 to compile. We should remove this once Python 3 support is fully
* supported and proper types are used in the extensions themselves. */
#define PyInt_Check PyLong_Check
#define PyInt_FromLong PyLong_FromLong
#define PyInt_FromSsize_t PyLong_FromSsize_t
#define PyInt_AsLong PyLong_AsLong
#endif
Yuya Nishihara
cext: mark constant variables
r32385 static const char *const versionerrortext = "Python minor version mismatch";
Yuya Nishihara
parsers: switch to policy importer...
r32372
static PyObject *dict_new_presized(PyObject *self, PyObject *args)
{
Py_ssize_t expected_size;
Augie Fackler
cleanup: use clang-tidy to add missing {} around one-line statements...
r41367 if (!PyArg_ParseTuple(args, "n:make_presized_dict", &expected_size)) {
Yuya Nishihara
parsers: switch to policy importer...
r32372 return NULL;
Augie Fackler
cleanup: use clang-tidy to add missing {} around one-line statements...
r41367 }
Yuya Nishihara
parsers: switch to policy importer...
r32372
return _dict_new_presized(expected_size);
}
static inline dirstateTupleObject *make_dirstate_tuple(char state, int mode,
Augie Fackler
parsers: allow clang-format here...
r34863 int size, int mtime)
Yuya Nishihara
parsers: switch to policy importer...
r32372 {
Augie Fackler
parsers: allow clang-format here...
r34863 dirstateTupleObject *t =
PyObject_New(dirstateTupleObject, &dirstateTupleType);
Augie Fackler
cleanup: use clang-tidy to add missing {} around one-line statements...
r41367 if (!t) {
Yuya Nishihara
parsers: switch to policy importer...
r32372 return NULL;
Augie Fackler
cleanup: use clang-tidy to add missing {} around one-line statements...
r41367 }
Yuya Nishihara
parsers: switch to policy importer...
r32372 t->state = state;
t->mode = mode;
t->size = size;
t->mtime = mtime;
return t;
}
static PyObject *dirstate_tuple_new(PyTypeObject *subtype, PyObject *args,
Augie Fackler
parsers: allow clang-format here...
r34863 PyObject *kwds)
Yuya Nishihara
parsers: switch to policy importer...
r32372 {
/* We do all the initialization here and not a tp_init function because
* dirstate_tuple is immutable. */
dirstateTupleObject *t;
char state;
int size, mode, mtime;
Augie Fackler
cleanup: use clang-tidy to add missing {} around one-line statements...
r41367 if (!PyArg_ParseTuple(args, "ciii", &state, &mode, &size, &mtime)) {
Yuya Nishihara
parsers: switch to policy importer...
r32372 return NULL;
Augie Fackler
cleanup: use clang-tidy to add missing {} around one-line statements...
r41367 }
Yuya Nishihara
parsers: switch to policy importer...
r32372
t = (dirstateTupleObject *)subtype->tp_alloc(subtype, 1);
Augie Fackler
cleanup: use clang-tidy to add missing {} around one-line statements...
r41367 if (!t) {
Yuya Nishihara
parsers: switch to policy importer...
r32372 return NULL;
Augie Fackler
cleanup: use clang-tidy to add missing {} around one-line statements...
r41367 }
Yuya Nishihara
parsers: switch to policy importer...
r32372 t->state = state;
t->mode = mode;
t->size = size;
t->mtime = mtime;
return (PyObject *)t;
}
static void dirstate_tuple_dealloc(PyObject *o)
{
PyObject_Del(o);
}
static Py_ssize_t dirstate_tuple_length(PyObject *o)
{
return 4;
}
static PyObject *dirstate_tuple_item(PyObject *o, Py_ssize_t i)
{
dirstateTupleObject *t = (dirstateTupleObject *)o;
switch (i) {
case 0:
return PyBytes_FromStringAndSize(&t->state, 1);
case 1:
return PyInt_FromLong(t->mode);
case 2:
return PyInt_FromLong(t->size);
case 3:
return PyInt_FromLong(t->mtime);
default:
PyErr_SetString(PyExc_IndexError, "index out of range");
return NULL;
}
}
static PySequenceMethods dirstate_tuple_sq = {
Augie Fackler
parsers: allow clang-format here...
r34863 dirstate_tuple_length, /* sq_length */
0, /* sq_concat */
0, /* sq_repeat */
dirstate_tuple_item, /* sq_item */
0, /* sq_ass_item */
0, /* sq_contains */
0, /* sq_inplace_concat */
0 /* sq_inplace_repeat */
Yuya Nishihara
parsers: switch to policy importer...
r32372 };
PyTypeObject dirstateTupleType = {
Augie Fackler
parsers: allow clang-format here...
r34863 PyVarObject_HEAD_INIT(NULL, 0) /* header */
"dirstate_tuple", /* tp_name */
sizeof(dirstateTupleObject), /* tp_basicsize */
0, /* tp_itemsize */
(destructor)dirstate_tuple_dealloc, /* tp_dealloc */
0, /* tp_print */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_compare */
0, /* tp_repr */
0, /* tp_as_number */
&dirstate_tuple_sq, /* 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 */
Py_TPFLAGS_DEFAULT, /* tp_flags */
"dirstate tuple", /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
0, /* 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 */
0, /* tp_init */
0, /* tp_alloc */
dirstate_tuple_new, /* tp_new */
Yuya Nishihara
parsers: switch to policy importer...
r32372 };
static PyObject *parse_dirstate(PyObject *self, PyObject *args)
{
PyObject *dmap, *cmap, *parents = NULL, *ret = NULL;
PyObject *fname = NULL, *cname = NULL, *entry = NULL;
char state, *cur, *str, *cpos;
int mode, size, mtime;
Gregory Szorc
cext: make parsers.c PY_SSIZE_T_CLEAN...
r42235 unsigned int flen, pos = 40;
Py_ssize_t len = 40;
Py_ssize_t readlen;
Yuya Nishihara
parsers: switch to policy importer...
r32372
Yuya Nishihara
py3: bulk-replace 'const char*' format specifier passed to PyArg_ParseTuple*()...
r36638 if (!PyArg_ParseTuple(
args, PY23("O!O!s#:parse_dirstate", "O!O!y#:parse_dirstate"),
Augie Fackler
cleanup: use clang-tidy to add missing {} around one-line statements...
r41367 &PyDict_Type, &dmap, &PyDict_Type, &cmap, &str, &readlen)) {
Yuya Nishihara
parsers: switch to policy importer...
r32372 goto quit;
Augie Fackler
cleanup: use clang-tidy to add missing {} around one-line statements...
r41367 }
Yuya Nishihara
parsers: switch to policy importer...
r32372
len = readlen;
/* read parents */
if (len < 40) {
Augie Fackler
parsers: allow clang-format here...
r34863 PyErr_SetString(PyExc_ValueError,
"too little data for parents");
Yuya Nishihara
parsers: switch to policy importer...
r32372 goto quit;
}
Yuya Nishihara
cext: cast s# arguments of Py_BuildValue() to Py_ssize_t...
r42265 parents = Py_BuildValue(PY23("s#s#", "y#y#"), str, (Py_ssize_t)20,
str + 20, (Py_ssize_t)20);
Augie Fackler
cleanup: use clang-tidy to add missing {} around one-line statements...
r41367 if (!parents) {
Yuya Nishihara
parsers: switch to policy importer...
r32372 goto quit;
Augie Fackler
cleanup: use clang-tidy to add missing {} around one-line statements...
r41367 }
Yuya Nishihara
parsers: switch to policy importer...
r32372
/* read filenames */
while (pos >= 40 && pos < len) {
if (pos + 17 > len) {
PyErr_SetString(PyExc_ValueError,
Augie Fackler
parsers: allow clang-format here...
r34863 "overflow in dirstate");
Yuya Nishihara
parsers: switch to policy importer...
r32372 goto quit;
}
cur = str + pos;
/* unpack header */
state = *cur;
mode = getbe32(cur + 1);
size = getbe32(cur + 5);
mtime = getbe32(cur + 9);
flen = getbe32(cur + 13);
pos += 17;
cur += 17;
if (flen > len - pos) {
Augie Fackler
parsers: allow clang-format here...
r34863 PyErr_SetString(PyExc_ValueError,
"overflow in dirstate");
Yuya Nishihara
parsers: switch to policy importer...
r32372 goto quit;
}
Augie Fackler
parsers: allow clang-format here...
r34863 entry =
(PyObject *)make_dirstate_tuple(state, mode, size, mtime);
Yuya Nishihara
parsers: switch to policy importer...
r32372 cpos = memchr(cur, 0, flen);
if (cpos) {
fname = PyBytes_FromStringAndSize(cur, cpos - cur);
Augie Fackler
parsers: allow clang-format here...
r34863 cname = PyBytes_FromStringAndSize(
cpos + 1, flen - (cpos - cur) - 1);
Yuya Nishihara
parsers: switch to policy importer...
r32372 if (!fname || !cname ||
PyDict_SetItem(cmap, fname, cname) == -1 ||
Augie Fackler
cleanup: use clang-tidy to add missing {} around one-line statements...
r41367 PyDict_SetItem(dmap, fname, entry) == -1) {
Yuya Nishihara
parsers: switch to policy importer...
r32372 goto quit;
Augie Fackler
cleanup: use clang-tidy to add missing {} around one-line statements...
r41367 }
Yuya Nishihara
parsers: switch to policy importer...
r32372 Py_DECREF(cname);
} else {
fname = PyBytes_FromStringAndSize(cur, flen);
Augie Fackler
cleanup: use clang-tidy to add missing {} around one-line statements...
r41367 if (!fname ||
PyDict_SetItem(dmap, fname, entry) == -1) {
Yuya Nishihara
parsers: switch to policy importer...
r32372 goto quit;
Augie Fackler
cleanup: use clang-tidy to add missing {} around one-line statements...
r41367 }
Yuya Nishihara
parsers: switch to policy importer...
r32372 }
Py_DECREF(fname);
Py_DECREF(entry);
fname = cname = entry = NULL;
pos += flen;
}
ret = parents;
Py_INCREF(ret);
quit:
Py_XDECREF(fname);
Py_XDECREF(cname);
Py_XDECREF(entry);
Py_XDECREF(parents);
return ret;
}
/*
* Build a set of non-normal and other parent entries from the dirstate dmap
Augie Fackler
parsers: allow clang-format here...
r34863 */
Gregory Szorc
cext: wrap before brace for functions...
r34441 static PyObject *nonnormalotherparententries(PyObject *self, PyObject *args)
{
Yuya Nishihara
parsers: switch to policy importer...
r32372 PyObject *dmap, *fname, *v;
PyObject *nonnset = NULL, *otherpset = NULL, *result = NULL;
Py_ssize_t pos;
Augie Fackler
cleanup: use clang-tidy to add missing {} around one-line statements...
r41367 if (!PyArg_ParseTuple(args, "O!:nonnormalentries", &PyDict_Type,
&dmap)) {
Yuya Nishihara
parsers: switch to policy importer...
r32372 goto bail;
Augie Fackler
cleanup: use clang-tidy to add missing {} around one-line statements...
r41367 }
Yuya Nishihara
parsers: switch to policy importer...
r32372
nonnset = PySet_New(NULL);
Augie Fackler
cleanup: use clang-tidy to add missing {} around one-line statements...
r41367 if (nonnset == NULL) {
Yuya Nishihara
parsers: switch to policy importer...
r32372 goto bail;
Augie Fackler
cleanup: use clang-tidy to add missing {} around one-line statements...
r41367 }
Yuya Nishihara
parsers: switch to policy importer...
r32372
otherpset = PySet_New(NULL);
Augie Fackler
cleanup: use clang-tidy to add missing {} around one-line statements...
r41367 if (otherpset == NULL) {
Yuya Nishihara
parsers: switch to policy importer...
r32372 goto bail;
Augie Fackler
cleanup: use clang-tidy to add missing {} around one-line statements...
r41367 }
Yuya Nishihara
parsers: switch to policy importer...
r32372
pos = 0;
while (PyDict_Next(dmap, &pos, &fname, &v)) {
dirstateTupleObject *t;
if (!dirstate_tuple_check(v)) {
PyErr_SetString(PyExc_TypeError,
Augie Fackler
parsers: allow clang-format here...
r34863 "expected a dirstate tuple");
Yuya Nishihara
parsers: switch to policy importer...
r32372 goto bail;
}
t = (dirstateTupleObject *)v;
if (t->state == 'n' && t->size == -2) {
if (PySet_Add(otherpset, fname) == -1) {
goto bail;
}
}
Augie Fackler
cleanup: use clang-tidy to add missing {} around one-line statements...
r41367 if (t->state == 'n' && t->mtime != -1) {
Yuya Nishihara
parsers: switch to policy importer...
r32372 continue;
Augie Fackler
cleanup: use clang-tidy to add missing {} around one-line statements...
r41367 }
if (PySet_Add(nonnset, fname) == -1) {
Yuya Nishihara
parsers: switch to policy importer...
r32372 goto bail;
Augie Fackler
cleanup: use clang-tidy to add missing {} around one-line statements...
r41367 }
Yuya Nishihara
parsers: switch to policy importer...
r32372 }
result = Py_BuildValue("(OO)", nonnset, otherpset);
Augie Fackler
cleanup: use clang-tidy to add missing {} around one-line statements...
r41367 if (result == NULL) {
Yuya Nishihara
parsers: switch to policy importer...
r32372 goto bail;
Augie Fackler
cleanup: use clang-tidy to add missing {} around one-line statements...
r41367 }
Yuya Nishihara
parsers: switch to policy importer...
r32372 Py_DECREF(nonnset);
Py_DECREF(otherpset);
return result;
bail:
Py_XDECREF(nonnset);
Py_XDECREF(otherpset);
Py_XDECREF(result);
return NULL;
}
/*
* Efficiently pack a dirstate object into its on-disk format.
*/
static PyObject *pack_dirstate(PyObject *self, PyObject *args)
{
PyObject *packobj = NULL;
PyObject *map, *copymap, *pl, *mtime_unset = NULL;
Py_ssize_t nbytes, pos, l;
PyObject *k, *v = NULL, *pn;
char *p, *s;
int now;
Yuya Nishihara
dirstate: use tuple interface to fix leak in pack_dirstate()...
r39485 if (!PyArg_ParseTuple(args, "O!O!O!i:pack_dirstate", &PyDict_Type, &map,
Augie Fackler
cleanup: use clang-tidy to add missing {} around one-line statements...
r41367 &PyDict_Type, &copymap, &PyTuple_Type, &pl,
&now)) {
Yuya Nishihara
parsers: switch to policy importer...
r32372 return NULL;
Augie Fackler
cleanup: use clang-tidy to add missing {} around one-line statements...
r41367 }
Yuya Nishihara
parsers: switch to policy importer...
r32372
Yuya Nishihara
dirstate: use tuple interface to fix leak in pack_dirstate()...
r39485 if (PyTuple_Size(pl) != 2) {
PyErr_SetString(PyExc_TypeError, "expected 2-element tuple");
Yuya Nishihara
parsers: switch to policy importer...
r32372 return NULL;
}
/* Figure out how much we need to allocate. */
for (nbytes = 40, pos = 0; PyDict_Next(map, &pos, &k, &v);) {
PyObject *c;
if (!PyBytes_Check(k)) {
PyErr_SetString(PyExc_TypeError, "expected string key");
goto bail;
}
nbytes += PyBytes_GET_SIZE(k) + 17;
c = PyDict_GetItem(copymap, k);
if (c) {
if (!PyBytes_Check(c)) {
PyErr_SetString(PyExc_TypeError,
Augie Fackler
parsers: allow clang-format here...
r34863 "expected string key");
Yuya Nishihara
parsers: switch to policy importer...
r32372 goto bail;
}
nbytes += PyBytes_GET_SIZE(c) + 1;
}
}
packobj = PyBytes_FromStringAndSize(NULL, nbytes);
Augie Fackler
cleanup: use clang-tidy to add missing {} around one-line statements...
r41367 if (packobj == NULL) {
Yuya Nishihara
parsers: switch to policy importer...
r32372 goto bail;
Augie Fackler
cleanup: use clang-tidy to add missing {} around one-line statements...
r41367 }
Yuya Nishihara
parsers: switch to policy importer...
r32372
p = PyBytes_AS_STRING(packobj);
Yuya Nishihara
dirstate: use tuple interface to fix leak in pack_dirstate()...
r39485 pn = PyTuple_GET_ITEM(pl, 0);
Yuya Nishihara
parsers: switch to policy importer...
r32372 if (PyBytes_AsStringAndSize(pn, &s, &l) == -1 || l != 20) {
PyErr_SetString(PyExc_TypeError, "expected a 20-byte hash");
goto bail;
}
memcpy(p, s, l);
p += 20;
Yuya Nishihara
dirstate: use tuple interface to fix leak in pack_dirstate()...
r39485 pn = PyTuple_GET_ITEM(pl, 1);
Yuya Nishihara
parsers: switch to policy importer...
r32372 if (PyBytes_AsStringAndSize(pn, &s, &l) == -1 || l != 20) {
PyErr_SetString(PyExc_TypeError, "expected a 20-byte hash");
goto bail;
}
memcpy(p, s, l);
p += 20;
Augie Fackler
parsers: allow clang-format here...
r34863 for (pos = 0; PyDict_Next(map, &pos, &k, &v);) {
Yuya Nishihara
parsers: switch to policy importer...
r32372 dirstateTupleObject *tuple;
char state;
int mode, size, mtime;
Py_ssize_t len, l;
PyObject *o;
char *t;
if (!dirstate_tuple_check(v)) {
PyErr_SetString(PyExc_TypeError,
Augie Fackler
parsers: allow clang-format here...
r34863 "expected a dirstate tuple");
Yuya Nishihara
parsers: switch to policy importer...
r32372 goto bail;
}
tuple = (dirstateTupleObject *)v;
state = tuple->state;
mode = tuple->mode;
size = tuple->size;
mtime = tuple->mtime;
if (state == 'n' && mtime == now) {
/* See pure/parsers.py:pack_dirstate for why we do
* this. */
mtime = -1;
mtime_unset = (PyObject *)make_dirstate_tuple(
Augie Fackler
parsers: allow clang-format here...
r34863 state, mode, size, mtime);
Augie Fackler
cleanup: use clang-tidy to add missing {} around one-line statements...
r41367 if (!mtime_unset) {
Yuya Nishihara
parsers: switch to policy importer...
r32372 goto bail;
Augie Fackler
cleanup: use clang-tidy to add missing {} around one-line statements...
r41367 }
if (PyDict_SetItem(map, k, mtime_unset) == -1) {
Yuya Nishihara
parsers: switch to policy importer...
r32372 goto bail;
Augie Fackler
cleanup: use clang-tidy to add missing {} around one-line statements...
r41367 }
Yuya Nishihara
parsers: switch to policy importer...
r32372 Py_DECREF(mtime_unset);
mtime_unset = NULL;
}
*p++ = state;
putbe32((uint32_t)mode, p);
putbe32((uint32_t)size, p + 4);
putbe32((uint32_t)mtime, p + 8);
t = p + 12;
p += 16;
len = PyBytes_GET_SIZE(k);
memcpy(p, PyBytes_AS_STRING(k), len);
p += len;
o = PyDict_GetItem(copymap, k);
if (o) {
*p++ = '\0';
l = PyBytes_GET_SIZE(o);
memcpy(p, PyBytes_AS_STRING(o), l);
p += l;
len += l + 1;
}
putbe32((uint32_t)len, t);
}
pos = p - PyBytes_AS_STRING(packobj);
if (pos != nbytes) {
PyErr_Format(PyExc_SystemError, "bad dirstate size: %ld != %ld",
Augie Fackler
parsers: allow clang-format here...
r34863 (long)pos, (long)nbytes);
Yuya Nishihara
parsers: switch to policy importer...
r32372 goto bail;
}
return packobj;
bail:
Py_XDECREF(mtime_unset);
Py_XDECREF(packobj);
Py_XDECREF(v);
return NULL;
}
#define BUMPED_FIX 1
#define USING_SHA_256 2
#define FM1_HEADER_SIZE (4 + 8 + 2 + 2 + 1 + 1 + 1)
Augie Fackler
parsers: allow clang-format here...
r34863 static PyObject *readshas(const char *source, unsigned char num,
Py_ssize_t hashwidth)
Yuya Nishihara
parsers: switch to policy importer...
r32372 {
int i;
PyObject *list = PyTuple_New(num);
if (list == NULL) {
return NULL;
}
for (i = 0; i < num; i++) {
PyObject *hash = PyBytes_FromStringAndSize(source, hashwidth);
if (hash == NULL) {
Py_DECREF(list);
return NULL;
}
PyTuple_SET_ITEM(list, i, hash);
source += hashwidth;
}
return list;
}
static PyObject *fm1readmarker(const char *databegin, const char *dataend,
Augie Fackler
parsers: allow clang-format here...
r34863 uint32_t *msize)
Yuya Nishihara
parsers: switch to policy importer...
r32372 {
const char *data = databegin;
const char *meta;
double mtime;
int16_t tz;
uint16_t flags;
unsigned char nsuccs, nparents, nmetadata;
Py_ssize_t hashwidth = 20;
PyObject *prec = NULL, *parents = NULL, *succs = NULL;
PyObject *metadata = NULL, *ret = NULL;
int i;
if (data + FM1_HEADER_SIZE > dataend) {
goto overflow;
}
*msize = getbe32(data);
data += 4;
mtime = getbefloat64(data);
data += 8;
tz = getbeint16(data);
data += 2;
flags = getbeuint16(data);
data += 2;
if (flags & USING_SHA_256) {
hashwidth = 32;
}
nsuccs = (unsigned char)(*data++);
nparents = (unsigned char)(*data++);
nmetadata = (unsigned char)(*data++);
if (databegin + *msize > dataend) {
goto overflow;
}
Augie Fackler
parsers: allow clang-format here...
r34863 dataend = databegin + *msize; /* narrow down to marker size */
Yuya Nishihara
parsers: switch to policy importer...
r32372
if (data + hashwidth > dataend) {
goto overflow;
}
prec = PyBytes_FromStringAndSize(data, hashwidth);
data += hashwidth;
if (prec == NULL) {
goto bail;
}
if (data + nsuccs * hashwidth > dataend) {
goto overflow;
}
succs = readshas(data, nsuccs, hashwidth);
if (succs == NULL) {
goto bail;
}
data += nsuccs * hashwidth;
if (nparents == 1 || nparents == 2) {
if (data + nparents * hashwidth > dataend) {
goto overflow;
}
parents = readshas(data, nparents, hashwidth);
if (parents == NULL) {
goto bail;
}
data += nparents * hashwidth;
} else {
parents = Py_None;
Py_INCREF(parents);
}
if (data + 2 * nmetadata > dataend) {
goto overflow;
}
meta = data + (2 * nmetadata);
metadata = PyTuple_New(nmetadata);
if (metadata == NULL) {
goto bail;
}
for (i = 0; i < nmetadata; i++) {
PyObject *tmp, *left = NULL, *right = NULL;
Py_ssize_t leftsize = (unsigned char)(*data++);
Py_ssize_t rightsize = (unsigned char)(*data++);
if (meta + leftsize + rightsize > dataend) {
goto overflow;
}
left = PyBytes_FromStringAndSize(meta, leftsize);
meta += leftsize;
right = PyBytes_FromStringAndSize(meta, rightsize);
meta += rightsize;
tmp = PyTuple_New(2);
if (!left || !right || !tmp) {
Py_XDECREF(left);
Py_XDECREF(right);
Py_XDECREF(tmp);
goto bail;
}
PyTuple_SET_ITEM(tmp, 0, left);
PyTuple_SET_ITEM(tmp, 1, right);
PyTuple_SET_ITEM(metadata, i, tmp);
}
Augie Fackler
parsers: allow clang-format here...
r34863 ret = Py_BuildValue("(OOHO(di)O)", prec, succs, flags, metadata, mtime,
(int)tz * 60, parents);
goto bail; /* return successfully */
Yuya Nishihara
parsers: switch to policy importer...
r32372
overflow:
PyErr_SetString(PyExc_ValueError, "overflow in obsstore");
bail:
Py_XDECREF(prec);
Py_XDECREF(succs);
Py_XDECREF(metadata);
Py_XDECREF(parents);
return ret;
}
Gregory Szorc
cext: wrap before brace for functions...
r34441 static PyObject *fm1readmarkers(PyObject *self, PyObject *args)
{
Yuya Nishihara
parsers: switch to policy importer...
r32372 const char *data, *dataend;
Gregory Szorc
cext: make parsers.c PY_SSIZE_T_CLEAN...
r42235 Py_ssize_t datalen, offset, stop;
Yuya Nishihara
parsers: switch to policy importer...
r32372 PyObject *markers = NULL;
Yuya Nishihara
py3: bulk-replace 'const char*' format specifier passed to PyArg_ParseTuple*()...
r36638 if (!PyArg_ParseTuple(args, PY23("s#nn", "y#nn"), &data, &datalen,
&offset, &stop)) {
Yuya Nishihara
parsers: switch to policy importer...
r32372 return NULL;
}
Augie Fackler
parsers: better bounds checking in fm1readmarkers...
r41052 if (offset < 0) {
PyErr_SetString(PyExc_ValueError,
"invalid negative offset in fm1readmarkers");
return NULL;
}
if (stop > datalen) {
PyErr_SetString(
PyExc_ValueError,
"stop longer than data length in fm1readmarkers");
return NULL;
}
Yuya Nishihara
parsers: switch to policy importer...
r32372 dataend = data + datalen;
data += offset;
markers = PyList_New(0);
if (!markers) {
return NULL;
}
while (offset < stop) {
uint32_t msize;
int error;
PyObject *record = fm1readmarker(data, dataend, &msize);
if (!record) {
goto bail;
}
error = PyList_Append(markers, record);
Py_DECREF(record);
if (error) {
goto bail;
}
data += msize;
offset += msize;
}
return markers;
bail:
Py_DECREF(markers);
return NULL;
}
static char parsers_doc[] = "Efficient content parsing.";
PyObject *encodedir(PyObject *self, PyObject *args);
PyObject *pathencode(PyObject *self, PyObject *args);
PyObject *lowerencode(PyObject *self, PyObject *args);
Gregory Szorc
cext: extract revlog/index parsing code to own C file...
r32378 PyObject *parse_index2(PyObject *self, PyObject *args);
Yuya Nishihara
parsers: switch to policy importer...
r32372
static PyMethodDef methods[] = {
Augie Fackler
parsers: allow clang-format here...
r34863 {"pack_dirstate", pack_dirstate, METH_VARARGS, "pack a dirstate\n"},
{"nonnormalotherparententries", nonnormalotherparententries, METH_VARARGS,
"create a set containing non-normal and other parent entries of given "
"dirstate\n"},
{"parse_dirstate", parse_dirstate, METH_VARARGS, "parse a dirstate\n"},
{"parse_index2", parse_index2, METH_VARARGS, "parse a revlog index\n"},
{"isasciistr", isasciistr, METH_VARARGS, "check if an ASCII string\n"},
{"asciilower", asciilower, METH_VARARGS, "lowercase an ASCII string\n"},
{"asciiupper", asciiupper, METH_VARARGS, "uppercase an ASCII string\n"},
{"dict_new_presized", dict_new_presized, METH_VARARGS,
"construct a dict with an expected size\n"},
{"make_file_foldmap", make_file_foldmap, METH_VARARGS,
"make file foldmap\n"},
{"jsonescapeu8fast", jsonescapeu8fast, METH_VARARGS,
"escape a UTF-8 byte string to JSON (fast path)\n"},
{"encodedir", encodedir, METH_VARARGS, "encodedir a path\n"},
{"pathencode", pathencode, METH_VARARGS, "fncache-encode a path\n"},
{"lowerencode", lowerencode, METH_VARARGS, "lower-encode a path\n"},
{"fm1readmarkers", fm1readmarkers, METH_VARARGS,
"parse v1 obsolete markers\n"},
{NULL, NULL}};
Yuya Nishihara
parsers: switch to policy importer...
r32372
void dirs_module_init(PyObject *mod);
void manifest_module_init(PyObject *mod);
Gregory Szorc
cext: extract revlog/index parsing code to own C file...
r32378 void revlog_module_init(PyObject *mod);
Yuya Nishihara
parsers: switch to policy importer...
r32372
Martin von Zweigbergk
util: make util.dirs() and util.finddirs() include root directory (API)...
r42530 static const int version = 13;
Yuya Nishihara
parsers: switch to policy importer...
r32372
static void module_init(PyObject *mod)
{
Raphaël Gomès
rust-dirstate: add rust-cpython bindings to the new parse/pack functions...
r42489 PyObject *capsule = NULL;
Yuya Nishihara
parsers: switch to policy importer...
r32372 PyModule_AddIntConstant(mod, "version", version);
/* This module constant has two purposes. First, it lets us unit test
* the ImportError raised without hard-coding any error text. This
* means we can change the text in the future without breaking tests,
* even across changesets without a recompile. Second, its presence
* can be used to determine whether the version-checking logic is
* present, which also helps in testing across changesets without a
* recompile. Note that this means the pure-Python version of parsers
* should not have this module constant. */
PyModule_AddStringConstant(mod, "versionerrortext", versionerrortext);
dirs_module_init(mod);
manifest_module_init(mod);
Gregory Szorc
cext: extract revlog/index parsing code to own C file...
r32378 revlog_module_init(mod);
Yuya Nishihara
parsers: switch to policy importer...
r32372
Raphaël Gomès
rust-dirstate: add rust-cpython bindings to the new parse/pack functions...
r42489 capsule = PyCapsule_New(
make_dirstate_tuple,
"mercurial.cext.parsers.make_dirstate_tuple_CAPI", NULL);
if (capsule != NULL)
PyModule_AddObject(mod, "make_dirstate_tuple_CAPI", capsule);
Augie Fackler
cleanup: use clang-tidy to add missing {} around one-line statements...
r41367 if (PyType_Ready(&dirstateTupleType) < 0) {
Yuya Nishihara
cext: move back finalization of dirstateTupleType where it should be
r32383 return;
Augie Fackler
cleanup: use clang-tidy to add missing {} around one-line statements...
r41367 }
Yuya Nishihara
parsers: switch to policy importer...
r32372 Py_INCREF(&dirstateTupleType);
PyModule_AddObject(mod, "dirstatetuple",
Augie Fackler
parsers: allow clang-format here...
r34863 (PyObject *)&dirstateTupleType);
Yuya Nishihara
parsers: switch to policy importer...
r32372 }
static int check_python_version(void)
{
PyObject *sys = PyImport_ImportModule("sys"), *ver;
long hexversion;
Augie Fackler
cleanup: use clang-tidy to add missing {} around one-line statements...
r41367 if (!sys) {
Yuya Nishihara
parsers: switch to policy importer...
r32372 return -1;
Augie Fackler
cleanup: use clang-tidy to add missing {} around one-line statements...
r41367 }
Yuya Nishihara
parsers: switch to policy importer...
r32372 ver = PyObject_GetAttrString(sys, "hexversion");
Py_DECREF(sys);
Augie Fackler
cleanup: use clang-tidy to add missing {} around one-line statements...
r41367 if (!ver) {
Yuya Nishihara
parsers: switch to policy importer...
r32372 return -1;
Augie Fackler
cleanup: use clang-tidy to add missing {} around one-line statements...
r41367 }
Yuya Nishihara
parsers: switch to policy importer...
r32372 hexversion = PyInt_AsLong(ver);
Py_DECREF(ver);
/* sys.hexversion is a 32-bit number by default, so the -1 case
* should only occur in unusual circumstances (e.g. if sys.hexversion
* is manually set to an invalid value). */
if ((hexversion == -1) || (hexversion >> 16 != PY_VERSION_HEX >> 16)) {
Augie Fackler
parsers: allow clang-format here...
r34863 PyErr_Format(PyExc_ImportError,
"%s: The Mercurial extension "
"modules were compiled with Python " PY_VERSION
", but "
"Mercurial is currently using Python with "
"sys.hexversion=%ld: "
"Python %s\n at: %s",
versionerrortext, hexversion, Py_GetVersion(),
Py_GetProgramFullPath());
Yuya Nishihara
parsers: switch to policy importer...
r32372 return -1;
}
return 0;
}
#ifdef IS_PY3K
Augie Fackler
parsers: allow clang-format here...
r34863 static struct PyModuleDef parsers_module = {PyModuleDef_HEAD_INIT, "parsers",
parsers_doc, -1, methods};
Yuya Nishihara
parsers: switch to policy importer...
r32372
PyMODINIT_FUNC PyInit_parsers(void)
{
PyObject *mod;
if (check_python_version() == -1)
return NULL;
mod = PyModule_Create(&parsers_module);
module_init(mod);
return mod;
}
#else
PyMODINIT_FUNC initparsers(void)
{
PyObject *mod;
Augie Fackler
cleanup: use clang-tidy to add missing {} around one-line statements...
r41367 if (check_python_version() == -1) {
Yuya Nishihara
parsers: switch to policy importer...
r32372 return;
Augie Fackler
cleanup: use clang-tidy to add missing {} around one-line statements...
r41367 }
Yuya Nishihara
parsers: switch to policy importer...
r32372 mod = Py_InitModule3("parsers", methods, parsers_doc);
module_init(mod);
}
#endif