parsers.c
2914 lines
| 69.4 KiB
| text/x-c
|
CLexer
/ mercurial / parsers.c
|
r6389 | /* | ||
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. | ||||
*/ | ||||
#include <Python.h> | ||||
#include <ctype.h> | ||||
|
r17356 | #include <stddef.h> | ||
|
r6389 | #include <string.h> | ||
|
r11361 | #include "util.h" | ||
|
r29444 | #include "bitmanipulation.h" | ||
|
r11361 | |||
|
r30112 | #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_Type PyLong_Type | ||||
|
r30169 | #define PyInt_Check PyLong_Check | ||
|
r30112 | #define PyInt_FromLong PyLong_FromLong | ||
|
r30169 | #define PyInt_FromSsize_t PyLong_FromSsize_t | ||
#define PyInt_AS_LONG PyLong_AS_LONG | ||||
|
r30112 | #define PyInt_AsLong PyLong_AsLong | ||
#endif | ||||
|
r20742 | static char *versionerrortext = "Python minor version mismatch"; | ||
|
r19718 | static int8_t hextable[256] = { | ||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, | ||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, | ||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, | ||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, /* 0-9 */ | ||||
-1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* A-F */ | ||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, | ||||
-1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* a-f */ | ||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, | ||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, | ||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, | ||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, | ||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, | ||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, | ||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, | ||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, | ||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 | ||||
}; | ||||
|
r22778 | static char lowertable[128] = { | ||
'\x00', '\x01', '\x02', '\x03', '\x04', '\x05', '\x06', '\x07', | ||||
'\x08', '\x09', '\x0a', '\x0b', '\x0c', '\x0d', '\x0e', '\x0f', | ||||
'\x10', '\x11', '\x12', '\x13', '\x14', '\x15', '\x16', '\x17', | ||||
'\x18', '\x19', '\x1a', '\x1b', '\x1c', '\x1d', '\x1e', '\x1f', | ||||
'\x20', '\x21', '\x22', '\x23', '\x24', '\x25', '\x26', '\x27', | ||||
'\x28', '\x29', '\x2a', '\x2b', '\x2c', '\x2d', '\x2e', '\x2f', | ||||
'\x30', '\x31', '\x32', '\x33', '\x34', '\x35', '\x36', '\x37', | ||||
'\x38', '\x39', '\x3a', '\x3b', '\x3c', '\x3d', '\x3e', '\x3f', | ||||
'\x40', | ||||
'\x61', '\x62', '\x63', '\x64', '\x65', '\x66', '\x67', /* A-G */ | ||||
'\x68', '\x69', '\x6a', '\x6b', '\x6c', '\x6d', '\x6e', '\x6f', /* H-O */ | ||||
'\x70', '\x71', '\x72', '\x73', '\x74', '\x75', '\x76', '\x77', /* P-W */ | ||||
'\x78', '\x79', '\x7a', /* X-Z */ | ||||
'\x5b', '\x5c', '\x5d', '\x5e', '\x5f', | ||||
'\x60', '\x61', '\x62', '\x63', '\x64', '\x65', '\x66', '\x67', | ||||
'\x68', '\x69', '\x6a', '\x6b', '\x6c', '\x6d', '\x6e', '\x6f', | ||||
'\x70', '\x71', '\x72', '\x73', '\x74', '\x75', '\x76', '\x77', | ||||
'\x78', '\x79', '\x7a', '\x7b', '\x7c', '\x7d', '\x7e', '\x7f' | ||||
}; | ||||
|
r24577 | static char uppertable[128] = { | ||
'\x00', '\x01', '\x02', '\x03', '\x04', '\x05', '\x06', '\x07', | ||||
'\x08', '\x09', '\x0a', '\x0b', '\x0c', '\x0d', '\x0e', '\x0f', | ||||
'\x10', '\x11', '\x12', '\x13', '\x14', '\x15', '\x16', '\x17', | ||||
'\x18', '\x19', '\x1a', '\x1b', '\x1c', '\x1d', '\x1e', '\x1f', | ||||
'\x20', '\x21', '\x22', '\x23', '\x24', '\x25', '\x26', '\x27', | ||||
'\x28', '\x29', '\x2a', '\x2b', '\x2c', '\x2d', '\x2e', '\x2f', | ||||
'\x30', '\x31', '\x32', '\x33', '\x34', '\x35', '\x36', '\x37', | ||||
'\x38', '\x39', '\x3a', '\x3b', '\x3c', '\x3d', '\x3e', '\x3f', | ||||
'\x40', '\x41', '\x42', '\x43', '\x44', '\x45', '\x46', '\x47', | ||||
'\x48', '\x49', '\x4a', '\x4b', '\x4c', '\x4d', '\x4e', '\x4f', | ||||
'\x50', '\x51', '\x52', '\x53', '\x54', '\x55', '\x56', '\x57', | ||||
'\x58', '\x59', '\x5a', '\x5b', '\x5c', '\x5d', '\x5e', '\x5f', | ||||
'\x60', | ||||
'\x41', '\x42', '\x43', '\x44', '\x45', '\x46', '\x47', /* a-g */ | ||||
'\x48', '\x49', '\x4a', '\x4b', '\x4c', '\x4d', '\x4e', '\x4f', /* h-o */ | ||||
'\x50', '\x51', '\x52', '\x53', '\x54', '\x55', '\x56', '\x57', /* p-w */ | ||||
'\x58', '\x59', '\x5a', /* x-z */ | ||||
'\x7b', '\x7c', '\x7d', '\x7e', '\x7f' | ||||
}; | ||||
|
r16617 | static inline int hexdigit(const char *p, Py_ssize_t off) | ||
|
r6389 | { | ||
|
r19718 | int8_t val = hextable[(unsigned char)p[off]]; | ||
|
r16617 | |||
|
r19718 | if (val >= 0) { | ||
return val; | ||||
} | ||||
|
r6389 | |||
|
r7092 | PyErr_SetString(PyExc_ValueError, "input contains non-hex character"); | ||
return 0; | ||||
|
r6389 | } | ||
/* | ||||
* Turn a hex-encoded string into binary. | ||||
*/ | ||||
|
r24214 | PyObject *unhexlify(const char *str, int len) | ||
|
r6389 | { | ||
|
r7092 | PyObject *ret; | ||
|
r6395 | char *d; | ||
|
r16617 | int i; | ||
|
r6389 | |||
|
r11361 | ret = PyBytes_FromStringAndSize(NULL, len / 2); | ||
|
r6389 | if (!ret) | ||
|
r7092 | return NULL; | ||
|
r6389 | |||
|
r11361 | d = PyBytes_AsString(ret); | ||
|
r16617 | for (i = 0; i < len;) { | ||
int hi = hexdigit(str, i++); | ||||
int lo = hexdigit(str, i++); | ||||
|
r6389 | *d++ = (hi << 4) | lo; | ||
} | ||||
|
r7091 | |||
|
r6389 | return ret; | ||
} | ||||
|
r24576 | static inline PyObject *_asciitransform(PyObject *str_obj, | ||
|
r24606 | const char table[128], | ||
PyObject *fallback_fn) | ||||
|
r22778 | { | ||
char *str, *newstr; | ||||
|
r24574 | Py_ssize_t i, len; | ||
|
r22778 | PyObject *newobj = NULL; | ||
|
r24575 | PyObject *ret = NULL; | ||
|
r22778 | |||
|
r24574 | str = PyBytes_AS_STRING(str_obj); | ||
len = PyBytes_GET_SIZE(str_obj); | ||||
|
r22778 | |||
newobj = PyBytes_FromStringAndSize(NULL, len); | ||||
if (!newobj) | ||||
goto quit; | ||||
newstr = PyBytes_AS_STRING(newobj); | ||||
for (i = 0; i < len; i++) { | ||||
char c = str[i]; | ||||
if (c & 0x80) { | ||||
|
r24606 | if (fallback_fn != NULL) { | ||
ret = PyObject_CallFunctionObjArgs(fallback_fn, | ||||
str_obj, NULL); | ||||
} else { | ||||
PyObject *err = PyUnicodeDecodeError_Create( | ||||
"ascii", str, len, i, (i + 1), | ||||
"unexpected code byte"); | ||||
PyErr_SetObject(PyExc_UnicodeDecodeError, err); | ||||
Py_XDECREF(err); | ||||
} | ||||
|
r22778 | goto quit; | ||
} | ||||
|
r24576 | newstr[i] = table[(unsigned char)c]; | ||
|
r22778 | } | ||
|
r24575 | ret = newobj; | ||
Py_INCREF(ret); | ||||
|
r22778 | quit: | ||
Py_XDECREF(newobj); | ||||
|
r24575 | return ret; | ||
|
r22778 | } | ||
|
r24574 | static PyObject *asciilower(PyObject *self, PyObject *args) | ||
{ | ||||
PyObject *str_obj; | ||||
if (!PyArg_ParseTuple(args, "O!:asciilower", &PyBytes_Type, &str_obj)) | ||||
return NULL; | ||||
|
r24606 | return _asciitransform(str_obj, lowertable, NULL); | ||
|
r24574 | } | ||
|
r24577 | static PyObject *asciiupper(PyObject *self, PyObject *args) | ||
{ | ||||
PyObject *str_obj; | ||||
if (!PyArg_ParseTuple(args, "O!:asciiupper", &PyBytes_Type, &str_obj)) | ||||
return NULL; | ||||
|
r24606 | return _asciitransform(str_obj, uppertable, NULL); | ||
|
r24577 | } | ||
|
r25583 | static inline PyObject *_dict_new_presized(Py_ssize_t expected_size) | ||
{ | ||||
/* _PyDict_NewPresized expects a minused parameter, but it actually | ||||
creates a dictionary that's the nearest power of two bigger than the | ||||
parameter. For example, with the initial minused = 1000, the | ||||
dictionary created has size 1024. Of course in a lot of cases that | ||||
can be greater than the maximum load factor Python's dict object | ||||
expects (= 2/3), so as soon as we cross the threshold we'll resize | ||||
anyway. So create a dictionary that's at least 3/2 the size. */ | ||||
return _PyDict_NewPresized(((1 + expected_size) / 2) * 3); | ||||
} | ||||
|
r25584 | static PyObject *dict_new_presized(PyObject *self, PyObject *args) | ||
{ | ||||
Py_ssize_t expected_size; | ||||
if (!PyArg_ParseTuple(args, "n:make_presized_dict", &expected_size)) | ||||
return NULL; | ||||
return _dict_new_presized(expected_size); | ||||
} | ||||
|
r24609 | static PyObject *make_file_foldmap(PyObject *self, PyObject *args) | ||
{ | ||||
PyObject *dmap, *spec_obj, *normcase_fallback; | ||||
PyObject *file_foldmap = NULL; | ||||
enum normcase_spec spec; | ||||
PyObject *k, *v; | ||||
dirstateTupleObject *tuple; | ||||
Py_ssize_t pos = 0; | ||||
const char *table; | ||||
if (!PyArg_ParseTuple(args, "O!O!O!:make_file_foldmap", | ||||
&PyDict_Type, &dmap, | ||||
&PyInt_Type, &spec_obj, | ||||
&PyFunction_Type, &normcase_fallback)) | ||||
goto quit; | ||||
|
r24622 | spec = (int)PyInt_AS_LONG(spec_obj); | ||
|
r24609 | switch (spec) { | ||
case NORMCASE_LOWER: | ||||
table = lowertable; | ||||
break; | ||||
case NORMCASE_UPPER: | ||||
table = uppertable; | ||||
break; | ||||
case NORMCASE_OTHER: | ||||
table = NULL; | ||||
break; | ||||
default: | ||||
PyErr_SetString(PyExc_TypeError, "invalid normcasespec"); | ||||
goto quit; | ||||
} | ||||
|
r25583 | /* Add some more entries to deal with additions outside this | ||
function. */ | ||||
file_foldmap = _dict_new_presized((PyDict_Size(dmap) / 10) * 11); | ||||
|
r24609 | if (file_foldmap == NULL) | ||
goto quit; | ||||
while (PyDict_Next(dmap, &pos, &k, &v)) { | ||||
if (!dirstate_tuple_check(v)) { | ||||
PyErr_SetString(PyExc_TypeError, | ||||
"expected a dirstate tuple"); | ||||
goto quit; | ||||
} | ||||
tuple = (dirstateTupleObject *)v; | ||||
if (tuple->state != 'r') { | ||||
PyObject *normed; | ||||
if (table != NULL) { | ||||
normed = _asciitransform(k, table, | ||||
normcase_fallback); | ||||
} else { | ||||
normed = PyObject_CallFunctionObjArgs( | ||||
normcase_fallback, k, NULL); | ||||
} | ||||
if (normed == NULL) | ||||
goto quit; | ||||
|
r26049 | if (PyDict_SetItem(file_foldmap, normed, k) == -1) { | ||
Py_DECREF(normed); | ||||
|
r24609 | goto quit; | ||
|
r26049 | } | ||
Py_DECREF(normed); | ||||
|
r24609 | } | ||
} | ||||
return file_foldmap; | ||||
quit: | ||||
Py_XDECREF(file_foldmap); | ||||
return NULL; | ||||
} | ||||
|
r6389 | /* | ||
* This code assumes that a manifest is stitched together with newline | ||||
* ('\n') characters. | ||||
*/ | ||||
static PyObject *parse_manifest(PyObject *self, PyObject *args) | ||||
{ | ||||
PyObject *mfdict, *fdict; | ||||
|
r19728 | char *str, *start, *end; | ||
|
r6389 | int len; | ||
if (!PyArg_ParseTuple(args, "O!O!s#:parse_manifest", | ||||
&PyDict_Type, &mfdict, | ||||
&PyDict_Type, &fdict, | ||||
&str, &len)) | ||||
goto quit; | ||||
|
r19728 | start = str; | ||
end = str + len; | ||||
while (start < end) { | ||||
|
r6389 | PyObject *file = NULL, *node = NULL; | ||
PyObject *flags = NULL; | ||||
|
r19728 | char *zero = NULL, *newline = NULL; | ||
|
r17356 | ptrdiff_t nlen; | ||
|
r6389 | |||
|
r19728 | zero = memchr(start, '\0', end - start); | ||
|
r6389 | if (!zero) { | ||
PyErr_SetString(PyExc_ValueError, | ||||
"manifest entry has no separator"); | ||||
goto quit; | ||||
} | ||||
|
r19728 | newline = memchr(zero + 1, '\n', end - (zero + 1)); | ||
if (!newline) { | ||||
PyErr_SetString(PyExc_ValueError, | ||||
"manifest contains trailing garbage"); | ||||
goto quit; | ||||
} | ||||
|
r11361 | file = PyBytes_FromStringAndSize(start, zero - start); | ||
|
r6389 | if (!file) | ||
goto bail; | ||||
|
r19728 | nlen = newline - zero - 1; | ||
|
r6389 | |||
|
r17356 | node = unhexlify(zero + 1, nlen > 40 ? 40 : (int)nlen); | ||
|
r6389 | if (!node) | ||
goto bail; | ||||
if (nlen > 40) { | ||||
|
r11361 | flags = PyBytes_FromStringAndSize(zero + 41, | ||
|
r6389 | nlen - 40); | ||
if (!flags) | ||||
goto bail; | ||||
if (PyDict_SetItem(fdict, file, flags) == -1) | ||||
goto bail; | ||||
} | ||||
if (PyDict_SetItem(mfdict, file, node) == -1) | ||||
goto bail; | ||||
|
r19728 | start = newline + 1; | ||
|
r6389 | |||
Py_XDECREF(flags); | ||||
Py_XDECREF(node); | ||||
Py_XDECREF(file); | ||||
continue; | ||||
bail: | ||||
Py_XDECREF(flags); | ||||
Py_XDECREF(node); | ||||
Py_XDECREF(file); | ||||
goto quit; | ||||
} | ||||
Py_INCREF(Py_None); | ||||
return Py_None; | ||||
quit: | ||||
return NULL; | ||||
} | ||||
|
r21809 | static inline dirstateTupleObject *make_dirstate_tuple(char state, int mode, | ||
int size, int mtime) | ||||
{ | ||||
dirstateTupleObject *t = PyObject_New(dirstateTupleObject, | ||||
&dirstateTupleType); | ||||
if (!t) | ||||
return NULL; | ||||
t->state = state; | ||||
t->mode = mode; | ||||
t->size = size; | ||||
t->mtime = mtime; | ||||
return t; | ||||
} | ||||
static PyObject *dirstate_tuple_new(PyTypeObject *subtype, PyObject *args, | ||||
PyObject *kwds) | ||||
{ | ||||
/* 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; | ||||
if (!PyArg_ParseTuple(args, "ciii", &state, &mode, &size, &mtime)) | ||||
return NULL; | ||||
t = (dirstateTupleObject *)subtype->tp_alloc(subtype, 1); | ||||
if (!t) | ||||
return NULL; | ||||
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 = { | ||||
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 */ | ||||
}; | ||||
PyTypeObject dirstateTupleType = { | ||||
PyVarObject_HEAD_INIT(NULL, 0) | ||||
"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 */ | ||||
}; | ||||
|
r7093 | static PyObject *parse_dirstate(PyObject *self, PyObject *args) | ||
{ | ||||
PyObject *dmap, *cmap, *parents = NULL, *ret = NULL; | ||||
PyObject *fname = NULL, *cname = NULL, *entry = NULL; | ||||
|
r20167 | char state, *cur, *str, *cpos; | ||
|
r19725 | int mode, size, mtime; | ||
|
r22403 | unsigned int flen, len, pos = 40; | ||
int readlen; | ||||
|
r7093 | |||
if (!PyArg_ParseTuple(args, "O!O!s#:parse_dirstate", | ||||
&PyDict_Type, &dmap, | ||||
&PyDict_Type, &cmap, | ||||
|
r22403 | &str, &readlen)) | ||
|
r7093 | goto quit; | ||
|
r22403 | len = readlen; | ||
|
r7093 | /* read parents */ | ||
|
r26052 | if (len < 40) { | ||
PyErr_SetString( | ||||
PyExc_ValueError, "too little data for parents"); | ||||
|
r7093 | goto quit; | ||
|
r26052 | } | ||
|
r7093 | |||
parents = Py_BuildValue("s#s#", str, 20, str + 20, 20); | ||||
if (!parents) | ||||
goto quit; | ||||
/* read filenames */ | ||||
|
r20167 | while (pos >= 40 && pos < len) { | ||
|
r27226 | if (pos + 17 > len) { | ||
PyErr_SetString(PyExc_ValueError, | ||||
"overflow in dirstate"); | ||||
goto quit; | ||||
} | ||||
|
r20167 | cur = str + pos; | ||
|
r7093 | /* unpack header */ | ||
state = *cur; | ||||
|
r16437 | mode = getbe32(cur + 1); | ||
size = getbe32(cur + 5); | ||||
mtime = getbe32(cur + 9); | ||||
flen = getbe32(cur + 13); | ||||
|
r20167 | pos += 17; | ||
|
r7093 | cur += 17; | ||
|
r20316 | if (flen > len - pos) { | ||
|
r7174 | PyErr_SetString(PyExc_ValueError, "overflow in dirstate"); | ||
|
r7093 | goto quit; | ||
|
r7174 | } | ||
|
r7093 | |||
|
r21809 | entry = (PyObject *)make_dirstate_tuple(state, mode, size, | ||
mtime); | ||||
|
r7093 | cpos = memchr(cur, 0, flen); | ||
if (cpos) { | ||||
|
r11361 | fname = PyBytes_FromStringAndSize(cur, cpos - cur); | ||
cname = PyBytes_FromStringAndSize(cpos + 1, | ||||
|
r7093 | flen - (cpos - cur) - 1); | ||
if (!fname || !cname || | ||||
PyDict_SetItem(cmap, fname, cname) == -1 || | ||||
PyDict_SetItem(dmap, fname, entry) == -1) | ||||
goto quit; | ||||
Py_DECREF(cname); | ||||
} else { | ||||
|
r11361 | fname = PyBytes_FromStringAndSize(cur, flen); | ||
|
r7093 | if (!fname || | ||
PyDict_SetItem(dmap, fname, entry) == -1) | ||||
goto quit; | ||||
} | ||||
Py_DECREF(fname); | ||||
Py_DECREF(entry); | ||||
fname = cname = entry = NULL; | ||||
|
r20167 | pos += flen; | ||
|
r7093 | } | ||
ret = parents; | ||||
Py_INCREF(ret); | ||||
quit: | ||||
Py_XDECREF(fname); | ||||
Py_XDECREF(cname); | ||||
Py_XDECREF(entry); | ||||
Py_XDECREF(parents); | ||||
return ret; | ||||
} | ||||
|
r16955 | /* | ||
|
r27592 | * Build a set of non-normal entries from the dirstate dmap | ||
*/ | ||||
static PyObject *nonnormalentries(PyObject *self, PyObject *args) | ||||
{ | ||||
PyObject *dmap, *nonnset = NULL, *fname, *v; | ||||
Py_ssize_t pos; | ||||
if (!PyArg_ParseTuple(args, "O!:nonnormalentries", | ||||
&PyDict_Type, &dmap)) | ||||
goto bail; | ||||
nonnset = PySet_New(NULL); | ||||
if (nonnset == NULL) | ||||
goto bail; | ||||
pos = 0; | ||||
while (PyDict_Next(dmap, &pos, &fname, &v)) { | ||||
dirstateTupleObject *t; | ||||
if (!dirstate_tuple_check(v)) { | ||||
PyErr_SetString(PyExc_TypeError, | ||||
"expected a dirstate tuple"); | ||||
goto bail; | ||||
} | ||||
t = (dirstateTupleObject *)v; | ||||
if (t->state == 'n' && t->mtime != -1) | ||||
continue; | ||||
if (PySet_Add(nonnset, fname) == -1) | ||||
goto bail; | ||||
} | ||||
return nonnset; | ||||
bail: | ||||
Py_XDECREF(nonnset); | ||||
return NULL; | ||||
} | ||||
/* | ||||
|
r16955 | * Efficiently pack a dirstate object into its on-disk format. | ||
*/ | ||||
static PyObject *pack_dirstate(PyObject *self, PyObject *args) | ||||
{ | ||||
PyObject *packobj = NULL; | ||||
|
r21806 | PyObject *map, *copymap, *pl, *mtime_unset = NULL; | ||
|
r16955 | Py_ssize_t nbytes, pos, l; | ||
|
r23946 | PyObject *k, *v = NULL, *pn; | ||
|
r16955 | char *p, *s; | ||
|
r26630 | int now; | ||
|
r16955 | |||
|
r26630 | if (!PyArg_ParseTuple(args, "O!O!Oi:pack_dirstate", | ||
|
r16955 | &PyDict_Type, &map, &PyDict_Type, ©map, | ||
&pl, &now)) | ||||
return NULL; | ||||
if (!PySequence_Check(pl) || PySequence_Size(pl) != 2) { | ||||
PyErr_SetString(PyExc_TypeError, "expected 2-element sequence"); | ||||
return NULL; | ||||
} | ||||
/* Figure out how much we need to allocate. */ | ||||
for (nbytes = 40, pos = 0; PyDict_Next(map, &pos, &k, &v);) { | ||||
PyObject *c; | ||||
|
r30100 | if (!PyBytes_Check(k)) { | ||
|
r16955 | PyErr_SetString(PyExc_TypeError, "expected string key"); | ||
goto bail; | ||||
} | ||||
|
r30100 | nbytes += PyBytes_GET_SIZE(k) + 17; | ||
|
r16955 | c = PyDict_GetItem(copymap, k); | ||
if (c) { | ||||
|
r30100 | if (!PyBytes_Check(c)) { | ||
|
r16955 | PyErr_SetString(PyExc_TypeError, | ||
"expected string key"); | ||||
goto bail; | ||||
} | ||||
|
r30100 | nbytes += PyBytes_GET_SIZE(c) + 1; | ||
|
r16955 | } | ||
} | ||||
|
r30100 | packobj = PyBytes_FromStringAndSize(NULL, nbytes); | ||
|
r16955 | if (packobj == NULL) | ||
goto bail; | ||||
|
r30100 | p = PyBytes_AS_STRING(packobj); | ||
|
r16955 | |||
pn = PySequence_ITEM(pl, 0); | ||||
|
r30100 | if (PyBytes_AsStringAndSize(pn, &s, &l) == -1 || l != 20) { | ||
|
r16955 | PyErr_SetString(PyExc_TypeError, "expected a 20-byte hash"); | ||
goto bail; | ||||
} | ||||
memcpy(p, s, l); | ||||
p += 20; | ||||
pn = PySequence_ITEM(pl, 1); | ||||
|
r30100 | if (PyBytes_AsStringAndSize(pn, &s, &l) == -1 || l != 20) { | ||
|
r16955 | PyErr_SetString(PyExc_TypeError, "expected a 20-byte hash"); | ||
goto bail; | ||||
} | ||||
memcpy(p, s, l); | ||||
p += 20; | ||||
for (pos = 0; PyDict_Next(map, &pos, &k, &v); ) { | ||||
|
r21809 | dirstateTupleObject *tuple; | ||
char state; | ||||
|
r26774 | int mode, size, mtime; | ||
|
r16955 | Py_ssize_t len, l; | ||
PyObject *o; | ||||
|
r21809 | char *t; | ||
|
r16955 | |||
|
r21809 | if (!dirstate_tuple_check(v)) { | ||
PyErr_SetString(PyExc_TypeError, | ||||
"expected a dirstate tuple"); | ||||
|
r16955 | goto bail; | ||
} | ||||
|
r21809 | tuple = (dirstateTupleObject *)v; | ||
state = tuple->state; | ||||
mode = tuple->mode; | ||||
size = tuple->size; | ||||
mtime = tuple->mtime; | ||||
|
r26630 | if (state == 'n' && mtime == now) { | ||
|
r18567 | /* See pure/parsers.py:pack_dirstate for why we do | ||
* this. */ | ||||
|
r21806 | mtime = -1; | ||
|
r21809 | mtime_unset = (PyObject *)make_dirstate_tuple( | ||
state, mode, size, mtime); | ||||
|
r21806 | if (!mtime_unset) | ||
|
r16955 | goto bail; | ||
|
r21806 | if (PyDict_SetItem(map, k, mtime_unset) == -1) | ||
goto bail; | ||||
Py_DECREF(mtime_unset); | ||||
mtime_unset = NULL; | ||||
|
r16955 | } | ||
|
r21809 | *p++ = state; | ||
|
r26774 | putbe32((uint32_t)mode, p); | ||
putbe32((uint32_t)size, p + 4); | ||||
putbe32((uint32_t)mtime, p + 8); | ||||
|
r16955 | t = p + 12; | ||
p += 16; | ||||
|
r30100 | len = PyBytes_GET_SIZE(k); | ||
memcpy(p, PyBytes_AS_STRING(k), len); | ||||
|
r16955 | p += len; | ||
o = PyDict_GetItem(copymap, k); | ||||
if (o) { | ||||
*p++ = '\0'; | ||||
|
r30100 | l = PyBytes_GET_SIZE(o); | ||
memcpy(p, PyBytes_AS_STRING(o), l); | ||||
|
r16955 | p += l; | ||
len += l + 1; | ||||
} | ||||
putbe32((uint32_t)len, t); | ||||
} | ||||
|
r30100 | pos = p - PyBytes_AS_STRING(packobj); | ||
|
r16955 | if (pos != nbytes) { | ||
PyErr_Format(PyExc_SystemError, "bad dirstate size: %ld != %ld", | ||||
(long)pos, (long)nbytes); | ||||
goto bail; | ||||
} | ||||
return packobj; | ||||
bail: | ||||
|
r21806 | Py_XDECREF(mtime_unset); | ||
|
r16955 | Py_XDECREF(packobj); | ||
|
r23946 | Py_XDECREF(v); | ||
|
r16955 | return NULL; | ||
} | ||||
|
r16363 | /* | ||
|
r16414 | * A base-16 trie for fast node->rev mapping. | ||
* | ||||
* Positive value is index of the next node in the trie | ||||
* Negative value is a leaf: -(rev + 1) | ||||
* Zero is empty | ||||
*/ | ||||
typedef struct { | ||||
int children[16]; | ||||
} nodetree; | ||||
/* | ||||
|
r26098 | * This class has two behaviors. | ||
|
r16414 | * | ||
* When used in a list-like way (with integer keys), we decode an | ||||
* entry in a RevlogNG index file on demand. Our last entry is a | ||||
* sentinel, always a nullid. We have limited support for | ||||
* integer-keyed insert and delete, only at elements right before the | ||||
* sentinel. | ||||
* | ||||
* With string keys, we lazily perform a reverse mapping from node to | ||||
* rev, using a base-16 trie. | ||||
|
r16363 | */ | ||
typedef struct { | ||||
PyObject_HEAD | ||||
/* Type-specific fields go here. */ | ||||
PyObject *data; /* raw bytes of index */ | ||||
PyObject **cache; /* cached tuples */ | ||||
const char **offsets; /* populated on demand */ | ||||
Py_ssize_t raw_length; /* original number of elements */ | ||||
Py_ssize_t length; /* current number of elements */ | ||||
PyObject *added; /* populated on demand */ | ||||
|
r16787 | PyObject *headrevs; /* cache, invalidated on changes */ | ||
|
r22484 | PyObject *filteredrevs;/* filtered revs set */ | ||
|
r16414 | nodetree *nt; /* base-16 trie */ | ||
|
r26075 | unsigned ntlength; /* # nodes in use */ | ||
unsigned ntcapacity; /* # nodes allocated */ | ||||
|
r16414 | int ntdepth; /* maximum depth of tree */ | ||
int ntsplits; /* # splits performed */ | ||||
int ntrev; /* last rev scanned */ | ||||
int ntlookups; /* # lookups */ | ||||
int ntmisses; /* # lookups that miss the cache */ | ||||
|
r16363 | int inlined; | ||
} indexObject; | ||||
|
r16414 | static Py_ssize_t index_length(const indexObject *self) | ||
|
r16363 | { | ||
if (self->added == NULL) | ||||
return self->length; | ||||
return self->length + PyList_GET_SIZE(self->added); | ||||
} | ||||
static PyObject *nullentry; | ||||
|
r16414 | static const char nullid[20]; | ||
|
r16363 | |||
|
r22401 | static Py_ssize_t inline_scan(indexObject *self, const char **offsets); | ||
|
r16363 | |||
#if LONG_MAX == 0x7fffffffL | ||||
|
r16393 | static char *tuple_format = "Kiiiiiis#"; | ||
|
r16363 | #else | ||
|
r16393 | static char *tuple_format = "kiiiiiis#"; | ||
|
r16363 | #endif | ||
|
r7108 | |||
|
r16863 | /* A RevlogNG v1 index entry is 64 bytes long. */ | ||
static const long v1_hdrsize = 64; | ||||
|
r16414 | /* | ||
* Return a pointer to the beginning of a RevlogNG record. | ||||
*/ | ||||
static const char *index_deref(indexObject *self, Py_ssize_t pos) | ||||
{ | ||||
if (self->inlined && pos > 0) { | ||||
if (self->offsets == NULL) { | ||||
self->offsets = malloc(self->raw_length * | ||||
sizeof(*self->offsets)); | ||||
if (self->offsets == NULL) | ||||
return (const char *)PyErr_NoMemory(); | ||||
inline_scan(self, self->offsets); | ||||
} | ||||
return self->offsets[pos]; | ||||
} | ||||
|
r30100 | return PyBytes_AS_STRING(self->data) + pos * v1_hdrsize; | ||
|
r16414 | } | ||
|
r25810 | static inline int index_get_parents(indexObject *self, Py_ssize_t rev, | ||
int *ps, int maxrev) | ||||
|
r25311 | { | ||
if (rev >= self->length - 1) { | ||||
PyObject *tuple = PyList_GET_ITEM(self->added, | ||||
rev - self->length + 1); | ||||
ps[0] = (int)PyInt_AS_LONG(PyTuple_GET_ITEM(tuple, 5)); | ||||
ps[1] = (int)PyInt_AS_LONG(PyTuple_GET_ITEM(tuple, 6)); | ||||
} else { | ||||
const char *data = index_deref(self, rev); | ||||
ps[0] = getbe32(data + 24); | ||||
ps[1] = getbe32(data + 28); | ||||
} | ||||
|
r25810 | /* If index file is corrupted, ps[] may point to invalid revisions. So | ||
* there is a risk of buffer overflow to trust them unconditionally. */ | ||||
if (ps[0] > maxrev || ps[1] > maxrev) { | ||||
PyErr_SetString(PyExc_ValueError, "parent out of range"); | ||||
return -1; | ||||
} | ||||
return 0; | ||||
|
r25311 | } | ||
|
r16414 | /* | ||
* RevlogNG format (all in big endian, data may be inlined): | ||||
|
r7108 | * 6 bytes: offset | ||
* 2 bytes: flags | ||||
* 4 bytes: compressed length | ||||
* 4 bytes: uncompressed length | ||||
* 4 bytes: base revision | ||||
* 4 bytes: link revision | ||||
* 4 bytes: parent 1 revision | ||||
* 4 bytes: parent 2 revision | ||||
* 32 bytes: nodeid (only 20 bytes used) | ||||
*/ | ||||
|
r16363 | static PyObject *index_get(indexObject *self, Py_ssize_t pos) | ||
|
r7108 | { | ||
|
r7154 | uint64_t offset_flags; | ||
|
r7108 | int comp_len, uncomp_len, base_rev, link_rev, parent_1, parent_2; | ||
|
r7154 | const char *c_node_id; | ||
|
r16363 | const char *data; | ||
Py_ssize_t length = index_length(self); | ||||
PyObject *entry; | ||||
|
r16414 | if (pos < 0) | ||
pos += length; | ||||
if (pos < 0 || pos >= length) { | ||||
|
r16363 | PyErr_SetString(PyExc_IndexError, "revlog index out of range"); | ||
return NULL; | ||||
} | ||||
if (pos == length - 1) { | ||||
Py_INCREF(nullentry); | ||||
return nullentry; | ||||
} | ||||
|
r7108 | |||
|
r16363 | if (pos >= self->length - 1) { | ||
PyObject *obj; | ||||
obj = PyList_GET_ITEM(self->added, pos - self->length + 1); | ||||
Py_INCREF(obj); | ||||
return obj; | ||||
} | ||||
if (self->cache) { | ||||
if (self->cache[pos]) { | ||||
Py_INCREF(self->cache[pos]); | ||||
return self->cache[pos]; | ||||
} | ||||
} else { | ||||
self->cache = calloc(self->raw_length, sizeof(PyObject *)); | ||||
if (self->cache == NULL) | ||||
return PyErr_NoMemory(); | ||||
} | ||||
|
r7190 | |||
|
r16414 | data = index_deref(self, pos); | ||
if (data == NULL) | ||||
return NULL; | ||||
|
r16363 | |||
|
r16437 | offset_flags = getbe32(data + 4); | ||
|
r16363 | if (pos == 0) /* mask out version number for the first entry */ | ||
offset_flags &= 0xFFFF; | ||||
else { | ||||
|
r16437 | uint32_t offset_high = getbe32(data); | ||
|
r16363 | offset_flags |= ((uint64_t)offset_high) << 32; | ||
} | ||||
|
r7154 | |||
|
r16437 | comp_len = getbe32(data + 8); | ||
uncomp_len = getbe32(data + 12); | ||||
base_rev = getbe32(data + 16); | ||||
link_rev = getbe32(data + 20); | ||||
parent_1 = getbe32(data + 24); | ||||
parent_2 = getbe32(data + 28); | ||||
|
r16363 | c_node_id = data + 32; | ||
entry = Py_BuildValue(tuple_format, offset_flags, comp_len, | ||||
|
r13254 | uncomp_len, base_rev, link_rev, | ||
parent_1, parent_2, c_node_id, 20); | ||||
|
r19726 | if (entry) { | ||
|
r16363 | PyObject_GC_UnTrack(entry); | ||
|
r19726 | Py_INCREF(entry); | ||
} | ||||
|
r16363 | |||
self->cache[pos] = entry; | ||||
return entry; | ||||
} | ||||
|
r16414 | /* | ||
* Return the 20-byte SHA of the node corresponding to the given rev. | ||||
*/ | ||||
static const char *index_node(indexObject *self, Py_ssize_t pos) | ||||
{ | ||||
Py_ssize_t length = index_length(self); | ||||
const char *data; | ||||
|
r16664 | if (pos == length - 1 || pos == INT_MAX) | ||
|
r16414 | return nullid; | ||
if (pos >= length) | ||||
return NULL; | ||||
if (pos >= self->length - 1) { | ||||
PyObject *tuple, *str; | ||||
tuple = PyList_GET_ITEM(self->added, pos - self->length + 1); | ||||
str = PyTuple_GetItem(tuple, 7); | ||||
|
r30100 | return str ? PyBytes_AS_STRING(str) : NULL; | ||
|
r16414 | } | ||
data = index_deref(self, pos); | ||||
return data ? data + 32 : NULL; | ||||
} | ||||
static int nt_insert(indexObject *self, const char *node, int rev); | ||||
static int node_check(PyObject *obj, char **node, Py_ssize_t *nodelen) | ||||
{ | ||||
|
r30100 | if (PyBytes_AsStringAndSize(obj, node, nodelen) == -1) | ||
|
r16414 | return -1; | ||
if (*nodelen == 20) | ||||
return 0; | ||||
PyErr_SetString(PyExc_ValueError, "20-byte hash required"); | ||||
return -1; | ||||
} | ||||
|
r16363 | static PyObject *index_insert(indexObject *self, PyObject *args) | ||
{ | ||||
|
r16414 | PyObject *obj; | ||
char *node; | ||||
|
r22604 | int index; | ||
Py_ssize_t len, nodelen; | ||||
|
r16363 | |||
|
r22604 | if (!PyArg_ParseTuple(args, "iO", &index, &obj)) | ||
|
r16363 | return NULL; | ||
if (!PyTuple_Check(obj) || PyTuple_GET_SIZE(obj) != 8) { | ||||
|
r16414 | PyErr_SetString(PyExc_TypeError, "8-tuple required"); | ||
|
r16363 | return NULL; | ||
} | ||||
|
r7108 | |||
|
r16414 | if (node_check(PyTuple_GET_ITEM(obj, 7), &node, &nodelen) == -1) | ||
|
r16363 | return NULL; | ||
len = index_length(self); | ||||
|
r22604 | if (index < 0) | ||
index += len; | ||||
|
r16363 | |||
|
r22604 | if (index != len - 1) { | ||
|
r16363 | PyErr_SetString(PyExc_IndexError, | ||
"insert only supported at index -1"); | ||||
return NULL; | ||||
} | ||||
if (self->added == NULL) { | ||||
self->added = PyList_New(0); | ||||
if (self->added == NULL) | ||||
return NULL; | ||||
} | ||||
if (PyList_Append(self->added, obj) == -1) | ||||
return NULL; | ||||
|
r13254 | |||
|
r16414 | if (self->nt) | ||
|
r22604 | nt_insert(self, node, index); | ||
|
r16414 | |||
|
r16787 | Py_CLEAR(self->headrevs); | ||
|
r16363 | Py_RETURN_NONE; | ||
} | ||||
|
r16370 | static void _index_clearcaches(indexObject *self) | ||
{ | ||||
if (self->cache) { | ||||
Py_ssize_t i; | ||||
|
r16732 | for (i = 0; i < self->raw_length; i++) | ||
Py_CLEAR(self->cache[i]); | ||||
|
r16370 | free(self->cache); | ||
self->cache = NULL; | ||||
} | ||||
if (self->offsets) { | ||||
free(self->offsets); | ||||
self->offsets = NULL; | ||||
} | ||||
|
r16414 | if (self->nt) { | ||
free(self->nt); | ||||
self->nt = NULL; | ||||
} | ||||
|
r16787 | Py_CLEAR(self->headrevs); | ||
|
r16370 | } | ||
static PyObject *index_clearcaches(indexObject *self) | ||||
{ | ||||
_index_clearcaches(self); | ||||
|
r16414 | self->ntlength = self->ntcapacity = 0; | ||
self->ntdepth = self->ntsplits = 0; | ||||
self->ntrev = -1; | ||||
self->ntlookups = self->ntmisses = 0; | ||||
|
r16370 | Py_RETURN_NONE; | ||
} | ||||
|
r16414 | static PyObject *index_stats(indexObject *self) | ||
{ | ||||
PyObject *obj = PyDict_New(); | ||||
|
r23948 | PyObject *t = NULL; | ||
|
r16414 | |||
if (obj == NULL) | ||||
return NULL; | ||||
#define istat(__n, __d) \ | ||||
r28792 | do { \ | |||
t = PyInt_FromSsize_t(self->__n); \ | ||||
if (!t) \ | ||||
goto bail; \ | ||||
if (PyDict_SetItemString(obj, __d, t) == -1) \ | ||||
goto bail; \ | ||||
Py_DECREF(t); \ | ||||
} while (0) | ||||
|
r16414 | |||
if (self->added) { | ||||
Py_ssize_t len = PyList_GET_SIZE(self->added); | ||||
|
r23948 | t = PyInt_FromSsize_t(len); | ||
if (!t) | ||||
|
r16414 | goto bail; | ||
|
r23948 | if (PyDict_SetItemString(obj, "index entries added", t) == -1) | ||
goto bail; | ||||
Py_DECREF(t); | ||||
|
r16414 | } | ||
if (self->raw_length != self->length - 1) | ||||
istat(raw_length, "revs on disk"); | ||||
istat(length, "revs in memory"); | ||||
istat(ntcapacity, "node trie capacity"); | ||||
istat(ntdepth, "node trie depth"); | ||||
istat(ntlength, "node trie count"); | ||||
istat(ntlookups, "node trie lookups"); | ||||
istat(ntmisses, "node trie misses"); | ||||
istat(ntrev, "node trie last rev scanned"); | ||||
istat(ntsplits, "node trie splits"); | ||||
#undef istat | ||||
return obj; | ||||
bail: | ||||
Py_XDECREF(obj); | ||||
|
r23948 | Py_XDECREF(t); | ||
|
r16414 | return NULL; | ||
} | ||||
|
r16787 | /* | ||
* When we cache a list, we want to be sure the caller can't mutate | ||||
* the cached copy. | ||||
*/ | ||||
static PyObject *list_copy(PyObject *list) | ||||
{ | ||||
Py_ssize_t len = PyList_GET_SIZE(list); | ||||
PyObject *newlist = PyList_New(len); | ||||
Py_ssize_t i; | ||||
if (newlist == NULL) | ||||
return NULL; | ||||
for (i = 0; i < len; i++) { | ||||
PyObject *obj = PyList_GET_ITEM(list, i); | ||||
Py_INCREF(obj); | ||||
PyList_SET_ITEM(newlist, i, obj); | ||||
} | ||||
return newlist; | ||||
} | ||||
|
r26107 | static int check_filter(PyObject *filter, Py_ssize_t arg) { | ||
|
r22484 | if (filter) { | ||
PyObject *arglist, *result; | ||||
int isfiltered; | ||||
|
r26107 | arglist = Py_BuildValue("(n)", arg); | ||
|
r22484 | if (!arglist) { | ||
return -1; | ||||
} | ||||
result = PyEval_CallObject(filter, arglist); | ||||
Py_DECREF(arglist); | ||||
if (!result) { | ||||
return -1; | ||||
} | ||||
/* PyObject_IsTrue returns 1 if true, 0 if false, -1 if error, | ||||
* same as this function, so we can just return it directly.*/ | ||||
isfiltered = PyObject_IsTrue(result); | ||||
Py_DECREF(result); | ||||
return isfiltered; | ||||
} else { | ||||
return 0; | ||||
} | ||||
} | ||||
|
r24443 | static Py_ssize_t add_roots_get_min(indexObject *self, PyObject *list, | ||
|
r24499 | Py_ssize_t marker, char *phases) | ||
|
r24443 | { | ||
PyObject *iter = NULL; | ||||
PyObject *iter_item = NULL; | ||||
Py_ssize_t min_idx = index_length(self) + 1; | ||||
long iter_item_long; | ||||
if (PyList_GET_SIZE(list) != 0) { | ||||
iter = PyObject_GetIter(list); | ||||
if (iter == NULL) | ||||
return -2; | ||||
while ((iter_item = PyIter_Next(iter))) | ||||
{ | ||||
iter_item_long = PyInt_AS_LONG(iter_item); | ||||
Py_DECREF(iter_item); | ||||
if (iter_item_long < min_idx) | ||||
min_idx = iter_item_long; | ||||
phases[iter_item_long] = marker; | ||||
} | ||||
Py_DECREF(iter); | ||||
} | ||||
return min_idx; | ||||
} | ||||
static inline void set_phase_from_parents(char *phases, int parent_1, | ||||
|
r24499 | int parent_2, Py_ssize_t i) | ||
|
r24443 | { | ||
if (parent_1 >= 0 && phases[parent_1] > phases[i]) | ||||
phases[i] = phases[parent_1]; | ||||
if (parent_2 >= 0 && phases[parent_2] > phases[i]) | ||||
phases[i] = phases[parent_2]; | ||||
} | ||||
|
r26053 | static PyObject *reachableroots2(indexObject *self, PyObject *args) | ||
|
r26004 | { | ||
/* Input */ | ||||
long minroot; | ||||
PyObject *includepatharg = NULL; | ||||
int includepath = 0; | ||||
|
r26053 | /* heads and roots are lists */ | ||
|
r26004 | PyObject *heads = NULL; | ||
PyObject *roots = NULL; | ||||
PyObject *reachable = NULL; | ||||
PyObject *val; | ||||
Py_ssize_t len = index_length(self) - 1; | ||||
long revnum; | ||||
Py_ssize_t k; | ||||
Py_ssize_t i; | ||||
|
r26042 | Py_ssize_t l; | ||
|
r26004 | int r; | ||
int parents[2]; | ||||
/* Internal data structure: | ||||
* tovisit: array of length len+1 (all revs + nullrev), filled upto lentovisit | ||||
|
r26044 | * revstates: array of length len+1 (all revs + nullrev) */ | ||
|
r26004 | int *tovisit = NULL; | ||
long lentovisit = 0; | ||||
|
r26054 | enum { RS_SEEN = 1, RS_ROOT = 2, RS_REACHABLE = 4 }; | ||
|
r26043 | char *revstates = NULL; | ||
|
r26004 | |||
/* Get arguments */ | ||||
|
r26009 | if (!PyArg_ParseTuple(args, "lO!O!O!", &minroot, &PyList_Type, &heads, | ||
|
r26053 | &PyList_Type, &roots, | ||
&PyBool_Type, &includepatharg)) | ||||
|
r26004 | goto bail; | ||
if (includepatharg == Py_True) | ||||
includepath = 1; | ||||
/* Initialize return set */ | ||||
|
r26055 | reachable = PyList_New(0); | ||
if (reachable == NULL) | ||||
|
r26004 | goto bail; | ||
/* Initialize internal datastructures */ | ||||
tovisit = (int *)malloc((len + 1) * sizeof(int)); | ||||
if (tovisit == NULL) { | ||||
|
r26008 | PyErr_NoMemory(); | ||
|
r26016 | goto bail; | ||
|
r26004 | } | ||
|
r26043 | revstates = (char *)calloc(len + 1, 1); | ||
if (revstates == NULL) { | ||||
|
r26008 | PyErr_NoMemory(); | ||
|
r26016 | goto bail; | ||
|
r26004 | } | ||
|
r26053 | l = PyList_GET_SIZE(roots); | ||
for (i = 0; i < l; i++) { | ||||
revnum = PyInt_AsLong(PyList_GET_ITEM(roots, i)); | ||||
if (revnum == -1 && PyErr_Occurred()) | ||||
goto bail; | ||||
/* If root is out of range, e.g. wdir(), it must be unreachable | ||||
* from heads. So we can just ignore it. */ | ||||
if (revnum + 1 < 0 || revnum + 1 >= len + 1) | ||||
continue; | ||||
revstates[revnum + 1] |= RS_ROOT; | ||||
} | ||||
|
r26004 | /* Populate tovisit with all the heads */ | ||
|
r26042 | l = PyList_GET_SIZE(heads); | ||
for (i = 0; i < l; i++) { | ||||
|
r26018 | revnum = PyInt_AsLong(PyList_GET_ITEM(heads, i)); | ||
if (revnum == -1 && PyErr_Occurred()) | ||||
goto bail; | ||||
|
r26017 | if (revnum + 1 < 0 || revnum + 1 >= len + 1) { | ||
PyErr_SetString(PyExc_IndexError, "head out of range"); | ||||
goto bail; | ||||
} | ||||
|
r26044 | if (!(revstates[revnum + 1] & RS_SEEN)) { | ||
|
r26080 | tovisit[lentovisit++] = (int)revnum; | ||
|
r26044 | revstates[revnum + 1] |= RS_SEEN; | ||
|
r26004 | } | ||
} | ||||
/* Visit the tovisit list and find the reachable roots */ | ||||
k = 0; | ||||
while (k < lentovisit) { | ||||
/* Add the node to reachable if it is a root*/ | ||||
revnum = tovisit[k++]; | ||||
|
r26053 | if (revstates[revnum + 1] & RS_ROOT) { | ||
|
r26054 | revstates[revnum + 1] |= RS_REACHABLE; | ||
|
r26053 | val = PyInt_FromLong(revnum); | ||
if (val == NULL) | ||||
goto bail; | ||||
|
r26058 | r = PyList_Append(reachable, val); | ||
|
r26053 | Py_DECREF(val); | ||
|
r26058 | if (r < 0) | ||
goto bail; | ||||
|
r26053 | if (includepath == 0) | ||
|
r26004 | continue; | ||
} | ||||
/* Add its parents to the list of nodes to visit */ | ||||
|
r26041 | if (revnum == -1) | ||
continue; | ||||
r = index_get_parents(self, revnum, parents, (int)len - 1); | ||||
if (r < 0) | ||||
goto bail; | ||||
for (i = 0; i < 2; i++) { | ||||
|
r26044 | if (!(revstates[parents[i] + 1] & RS_SEEN) | ||
|
r26041 | && parents[i] >= minroot) { | ||
tovisit[lentovisit++] = parents[i]; | ||||
|
r26044 | revstates[parents[i] + 1] |= RS_SEEN; | ||
|
r26004 | } | ||
} | ||||
} | ||||
/* Find all the nodes in between the roots we found and the heads | ||||
* and add them to the reachable set */ | ||||
if (includepath == 1) { | ||||
|
r26080 | long minidx = minroot; | ||
|
r26004 | if (minidx < 0) | ||
minidx = 0; | ||||
for (i = minidx; i < len; i++) { | ||||
|
r26044 | if (!(revstates[i + 1] & RS_SEEN)) | ||
|
r26041 | continue; | ||
r = index_get_parents(self, i, parents, (int)len - 1); | ||||
/* Corrupted index file, error is set from | ||||
* index_get_parents */ | ||||
if (r < 0) | ||||
goto bail; | ||||
|
r26059 | if (((revstates[parents[0] + 1] | | ||
revstates[parents[1] + 1]) & RS_REACHABLE) | ||||
&& !(revstates[i + 1] & RS_REACHABLE)) { | ||||
revstates[i + 1] |= RS_REACHABLE; | ||||
val = PyInt_FromLong(i); | ||||
if (val == NULL) | ||||
goto bail; | ||||
r = PyList_Append(reachable, val); | ||||
Py_DECREF(val); | ||||
if (r < 0) | ||||
goto bail; | ||||
|
r26004 | } | ||
} | ||||
} | ||||
|
r26043 | free(revstates); | ||
|
r26004 | free(tovisit); | ||
return reachable; | ||||
|
r26016 | bail: | ||
|
r26004 | Py_XDECREF(reachable); | ||
|
r26043 | free(revstates); | ||
|
r26016 | free(tovisit); | ||
|
r26010 | return NULL; | ||
|
r26004 | } | ||
|
r25190 | static PyObject *compute_phases_map_sets(indexObject *self, PyObject *args) | ||
|
r24443 | { | ||
PyObject *roots = Py_None; | ||||
|
r25190 | PyObject *ret = NULL; | ||
|
r24443 | PyObject *phaseslist = NULL; | ||
PyObject *phaseroots = NULL; | ||||
|
r25190 | PyObject *phaseset = NULL; | ||
PyObject *phasessetlist = NULL; | ||||
|
r25911 | PyObject *rev = NULL; | ||
|
r24443 | Py_ssize_t len = index_length(self) - 1; | ||
Py_ssize_t numphase = 0; | ||||
Py_ssize_t minrevallphases = 0; | ||||
Py_ssize_t minrevphase = 0; | ||||
Py_ssize_t i = 0; | ||||
char *phases = NULL; | ||||
|
r25190 | long phase; | ||
|
r24443 | |||
if (!PyArg_ParseTuple(args, "O", &roots)) | ||||
|
r27364 | goto done; | ||
|
r24443 | if (roots == NULL || !PyList_Check(roots)) | ||
|
r27364 | goto done; | ||
|
r24443 | |||
phases = calloc(len, 1); /* phase per rev: {0: public, 1: draft, 2: secret} */ | ||||
|
r27364 | if (phases == NULL) { | ||
PyErr_NoMemory(); | ||||
goto done; | ||||
} | ||||
|
r24443 | /* Put the phase information of all the roots in phases */ | ||
numphase = PyList_GET_SIZE(roots)+1; | ||||
minrevallphases = len + 1; | ||||
|
r25190 | phasessetlist = PyList_New(numphase); | ||
if (phasessetlist == NULL) | ||||
|
r27364 | goto done; | ||
|
r25190 | |||
PyList_SET_ITEM(phasessetlist, 0, Py_None); | ||||
Py_INCREF(Py_None); | ||||
|
r24443 | for (i = 0; i < numphase-1; i++) { | ||
phaseroots = PyList_GET_ITEM(roots, i); | ||||
|
r25190 | phaseset = PySet_New(NULL); | ||
if (phaseset == NULL) | ||||
|
r27364 | goto release; | ||
|
r25190 | PyList_SET_ITEM(phasessetlist, i+1, phaseset); | ||
|
r24443 | if (!PyList_Check(phaseroots)) | ||
|
r27364 | goto release; | ||
|
r24443 | minrevphase = add_roots_get_min(self, phaseroots, i+1, phases); | ||
if (minrevphase == -2) /* Error from add_roots_get_min */ | ||||
|
r27364 | goto release; | ||
|
r24443 | minrevallphases = MIN(minrevallphases, minrevphase); | ||
} | ||||
/* Propagate the phase information from the roots to the revs */ | ||||
if (minrevallphases != -1) { | ||||
|
r25312 | int parents[2]; | ||
for (i = minrevallphases; i < len; i++) { | ||||
|
r25860 | if (index_get_parents(self, i, parents, | ||
(int)len - 1) < 0) | ||||
|
r27364 | goto release; | ||
|
r25312 | set_phase_from_parents(phases, parents[0], parents[1], i); | ||
|
r24443 | } | ||
} | ||||
/* Transform phase list to a python list */ | ||||
phaseslist = PyList_New(len); | ||||
if (phaseslist == NULL) | ||||
|
r27364 | goto release; | ||
|
r25190 | for (i = 0; i < len; i++) { | ||
|
r27365 | PyObject *phaseval; | ||
|
r25190 | phase = phases[i]; | ||
/* We only store the sets of phase for non public phase, the public phase | ||||
* is computed as a difference */ | ||||
if (phase != 0) { | ||||
phaseset = PyList_GET_ITEM(phasessetlist, phase); | ||||
|
r25911 | rev = PyInt_FromLong(i); | ||
|
r27365 | if (rev == NULL) | ||
goto release; | ||||
|
r25911 | PySet_Add(phaseset, rev); | ||
Py_XDECREF(rev); | ||||
|
r25190 | } | ||
|
r27365 | phaseval = PyInt_FromLong(phase); | ||
if (phaseval == NULL) | ||||
goto release; | ||||
PyList_SET_ITEM(phaseslist, i, phaseval); | ||||
|
r25190 | } | ||
|
r27410 | ret = PyTuple_Pack(2, phaseslist, phasessetlist); | ||
|
r25190 | |||
|
r27364 | release: | ||
|
r25190 | Py_XDECREF(phaseslist); | ||
Py_XDECREF(phasessetlist); | ||||
|
r27364 | done: | ||
|
r24443 | free(phases); | ||
|
r25190 | return ret; | ||
|
r24443 | } | ||
|
r22484 | static PyObject *index_headrevs(indexObject *self, PyObject *args) | ||
|
r16786 | { | ||
|
r25297 | Py_ssize_t i, j, len; | ||
|
r16786 | char *nothead = NULL; | ||
|
r22540 | PyObject *heads = NULL; | ||
|
r22484 | PyObject *filter = NULL; | ||
PyObject *filteredrevs = Py_None; | ||||
|
r16786 | |||
|
r22484 | if (!PyArg_ParseTuple(args, "|O", &filteredrevs)) { | ||
return NULL; | ||||
} | ||||
if (self->headrevs && filteredrevs == self->filteredrevs) | ||||
|
r16787 | return list_copy(self->headrevs); | ||
|
r22484 | Py_DECREF(self->filteredrevs); | ||
self->filteredrevs = filteredrevs; | ||||
Py_INCREF(filteredrevs); | ||||
if (filteredrevs != Py_None) { | ||||
filter = PyObject_GetAttrString(filteredrevs, "__contains__"); | ||||
if (!filter) { | ||||
PyErr_SetString(PyExc_TypeError, | ||||
"filteredrevs has no attribute __contains__"); | ||||
goto bail; | ||||
} | ||||
} | ||||
|
r16786 | len = index_length(self) - 1; | ||
heads = PyList_New(0); | ||||
if (heads == NULL) | ||||
goto bail; | ||||
if (len == 0) { | ||||
PyObject *nullid = PyInt_FromLong(-1); | ||||
if (nullid == NULL || PyList_Append(heads, nullid) == -1) { | ||||
Py_XDECREF(nullid); | ||||
goto bail; | ||||
} | ||||
goto done; | ||||
} | ||||
nothead = calloc(len, 1); | ||||
|
r27366 | if (nothead == NULL) { | ||
PyErr_NoMemory(); | ||||
|
r16786 | goto bail; | ||
|
r27366 | } | ||
|
r16786 | |||
|
r28386 | for (i = len - 1; i >= 0; i--) { | ||
|
r25297 | int isfiltered; | ||
int parents[2]; | ||||
|
r22484 | |||
|
r28386 | /* If nothead[i] == 1, it means we've seen an unfiltered child of this | ||
* node already, and therefore this node is not filtered. So we can skip | ||||
* the expensive check_filter step. | ||||
*/ | ||||
if (nothead[i] != 1) { | ||||
isfiltered = check_filter(filter, i); | ||||
if (isfiltered == -1) { | ||||
PyErr_SetString(PyExc_TypeError, | ||||
"unable to check filter"); | ||||
goto bail; | ||||
} | ||||
if (isfiltered) { | ||||
nothead[i] = 1; | ||||
continue; | ||||
} | ||||
|
r22484 | } | ||
|
r25860 | if (index_get_parents(self, i, parents, (int)len - 1) < 0) | ||
|
r25810 | goto bail; | ||
|
r25297 | for (j = 0; j < 2; j++) { | ||
if (parents[j] >= 0) | ||||
nothead[parents[j]] = 1; | ||||
|
r16786 | } | ||
} | ||||
for (i = 0; i < len; i++) { | ||||
PyObject *head; | ||||
if (nothead[i]) | ||||
continue; | ||||
|
r22400 | head = PyInt_FromSsize_t(i); | ||
|
r16786 | if (head == NULL || PyList_Append(heads, head) == -1) { | ||
Py_XDECREF(head); | ||||
goto bail; | ||||
} | ||||
} | ||||
done: | ||||
|
r16787 | self->headrevs = heads; | ||
|
r22484 | Py_XDECREF(filter); | ||
|
r16786 | free(nothead); | ||
|
r16787 | return list_copy(self->headrevs); | ||
|
r16786 | bail: | ||
|
r22484 | Py_XDECREF(filter); | ||
|
r16786 | Py_XDECREF(heads); | ||
free(nothead); | ||||
return NULL; | ||||
} | ||||
|
r16618 | static inline int nt_level(const char *node, Py_ssize_t level) | ||
|
r16414 | { | ||
int v = node[level>>1]; | ||||
if (!(level & 1)) | ||||
v >>= 4; | ||||
return v & 0xf; | ||||
} | ||||
|
r16616 | /* | ||
* Return values: | ||||
* | ||||
* -4: match is ambiguous (multiple candidates) | ||||
* -2: not found | ||||
* rest: valid rev | ||||
*/ | ||||
|
r16663 | static int nt_find(indexObject *self, const char *node, Py_ssize_t nodelen, | ||
int hex) | ||||
|
r16414 | { | ||
|
r16663 | int (*getnybble)(const char *, Py_ssize_t) = hex ? hexdigit : nt_level; | ||
|
r16641 | int level, maxlevel, off; | ||
|
r16414 | |||
if (nodelen == 20 && node[0] == '\0' && memcmp(node, nullid, 20) == 0) | ||||
return -1; | ||||
if (self->nt == NULL) | ||||
return -2; | ||||
|
r16663 | if (hex) | ||
|
r16665 | maxlevel = nodelen > 40 ? 40 : (int)nodelen; | ||
|
r16663 | else | ||
maxlevel = nodelen > 20 ? 40 : ((int)nodelen * 2); | ||||
|
r16641 | |||
for (level = off = 0; level < maxlevel; level++) { | ||||
|
r16663 | int k = getnybble(node, level); | ||
|
r16414 | nodetree *n = &self->nt[off]; | ||
int v = n->children[k]; | ||||
if (v < 0) { | ||||
const char *n; | ||||
|
r16663 | Py_ssize_t i; | ||
|
r24879 | v = -(v + 1); | ||
|
r16414 | n = index_node(self, v); | ||
if (n == NULL) | ||||
return -2; | ||||
|
r16663 | for (i = level; i < maxlevel; i++) | ||
if (getnybble(node, i) != nt_level(n, i)) | ||||
return -2; | ||||
return v; | ||||
|
r16414 | } | ||
if (v == 0) | ||||
return -2; | ||||
off = v; | ||||
} | ||||
|
r16616 | /* multiple matches against an ambiguous prefix */ | ||
return -4; | ||||
|
r16414 | } | ||
static int nt_new(indexObject *self) | ||||
{ | ||||
if (self->ntlength == self->ntcapacity) { | ||||
|
r24623 | if (self->ntcapacity >= INT_MAX / (sizeof(nodetree) * 2)) { | ||
PyErr_SetString(PyExc_MemoryError, | ||||
"overflow in nt_new"); | ||||
return -1; | ||||
} | ||||
|
r16414 | self->ntcapacity *= 2; | ||
self->nt = realloc(self->nt, | ||||
self->ntcapacity * sizeof(nodetree)); | ||||
if (self->nt == NULL) { | ||||
PyErr_SetString(PyExc_MemoryError, "out of memory"); | ||||
return -1; | ||||
} | ||||
memset(&self->nt[self->ntlength], 0, | ||||
sizeof(nodetree) * (self->ntcapacity - self->ntlength)); | ||||
} | ||||
return self->ntlength++; | ||||
} | ||||
static int nt_insert(indexObject *self, const char *node, int rev) | ||||
{ | ||||
int level = 0; | ||||
int off = 0; | ||||
|
r16641 | while (level < 40) { | ||
|
r16414 | int k = nt_level(node, level); | ||
nodetree *n; | ||||
int v; | ||||
n = &self->nt[off]; | ||||
v = n->children[k]; | ||||
if (v == 0) { | ||||
n->children[k] = -rev - 1; | ||||
return 0; | ||||
} | ||||
if (v < 0) { | ||||
|
r24879 | const char *oldnode = index_node(self, -(v + 1)); | ||
|
r16414 | int noff; | ||
if (!oldnode || !memcmp(oldnode, node, 20)) { | ||||
n->children[k] = -rev - 1; | ||||
return 0; | ||||
} | ||||
noff = nt_new(self); | ||||
if (noff == -1) | ||||
return -1; | ||||
/* self->nt may have been changed by realloc */ | ||||
self->nt[off].children[k] = noff; | ||||
off = noff; | ||||
n = &self->nt[off]; | ||||
n->children[nt_level(oldnode, ++level)] = v; | ||||
if (level > self->ntdepth) | ||||
self->ntdepth = level; | ||||
self->ntsplits += 1; | ||||
} else { | ||||
level += 1; | ||||
off = v; | ||||
} | ||||
} | ||||
return -1; | ||||
} | ||||
|
r16615 | static int nt_init(indexObject *self) | ||
{ | ||||
if (self->nt == NULL) { | ||||
|
r26775 | if ((size_t)self->raw_length > INT_MAX / sizeof(nodetree)) { | ||
|
r20110 | PyErr_SetString(PyExc_ValueError, "overflow in nt_init"); | ||
return -1; | ||||
} | ||||
|
r16615 | self->ntcapacity = self->raw_length < 4 | ||
|
r20110 | ? 4 : (int)self->raw_length / 2; | ||
|
r16615 | self->nt = calloc(self->ntcapacity, sizeof(nodetree)); | ||
if (self->nt == NULL) { | ||||
PyErr_NoMemory(); | ||||
return -1; | ||||
} | ||||
self->ntlength = 1; | ||||
self->ntrev = (int)index_length(self) - 1; | ||||
self->ntlookups = 1; | ||||
self->ntmisses = 0; | ||||
|
r16664 | if (nt_insert(self, nullid, INT_MAX) == -1) | ||
return -1; | ||||
|
r16615 | } | ||
return 0; | ||||
} | ||||
|
r16414 | /* | ||
* Return values: | ||||
* | ||||
* -3: error (exception set) | ||||
* -2: not found (no exception set) | ||||
* rest: valid rev | ||||
*/ | ||||
static int index_find_node(indexObject *self, | ||||
const char *node, Py_ssize_t nodelen) | ||||
{ | ||||
int rev; | ||||
self->ntlookups++; | ||||
|
r16663 | rev = nt_find(self, node, nodelen, 0); | ||
|
r16414 | if (rev >= -1) | ||
return rev; | ||||
|
r16615 | if (nt_init(self) == -1) | ||
return -3; | ||||
|
r16414 | |||
/* | ||||
* For the first handful of lookups, we scan the entire index, | ||||
* and cache only the matching nodes. This optimizes for cases | ||||
* like "hg tip", where only a few nodes are accessed. | ||||
* | ||||
* After that, we cache every node we visit, using a single | ||||
* scan amortized over multiple lookups. This gives the best | ||||
* bulk performance, e.g. for "hg log". | ||||
*/ | ||||
if (self->ntmisses++ < 4) { | ||||
for (rev = self->ntrev - 1; rev >= 0; rev--) { | ||||
const char *n = index_node(self, rev); | ||||
if (n == NULL) | ||||
return -2; | ||||
if (memcmp(node, n, nodelen > 20 ? 20 : nodelen) == 0) { | ||||
if (nt_insert(self, n, rev) == -1) | ||||
return -3; | ||||
break; | ||||
} | ||||
} | ||||
} else { | ||||
for (rev = self->ntrev - 1; rev >= 0; rev--) { | ||||
const char *n = index_node(self, rev); | ||||
|
r16614 | if (n == NULL) { | ||
self->ntrev = rev + 1; | ||||
|
r16414 | return -2; | ||
|
r16614 | } | ||
if (nt_insert(self, n, rev) == -1) { | ||||
self->ntrev = rev + 1; | ||||
|
r16414 | return -3; | ||
|
r16614 | } | ||
|
r16414 | if (memcmp(node, n, nodelen > 20 ? 20 : nodelen) == 0) { | ||
break; | ||||
} | ||||
} | ||||
self->ntrev = rev; | ||||
} | ||||
if (rev >= 0) | ||||
return rev; | ||||
return -2; | ||||
} | ||||
|
r25561 | static void raise_revlog_error(void) | ||
|
r16414 | { | ||
|
r25561 | PyObject *mod = NULL, *dict = NULL, *errclass = NULL; | ||
|
r16414 | |||
|
r25561 | mod = PyImport_ImportModule("mercurial.error"); | ||
if (mod == NULL) { | ||||
goto cleanup; | ||||
|
r16414 | } | ||
|
r25561 | dict = PyModule_GetDict(mod); | ||
if (dict == NULL) { | ||||
goto cleanup; | ||||
} | ||||
Py_INCREF(dict); | ||||
|
r16414 | |||
|
r25561 | errclass = PyDict_GetItemString(dict, "RevlogError"); | ||
if (errclass == NULL) { | ||||
PyErr_SetString(PyExc_SystemError, | ||||
"could not find RevlogError"); | ||||
goto cleanup; | ||||
} | ||||
/* value of exception is ignored by callers */ | ||||
PyErr_SetString(errclass, "RevlogError"); | ||||
cleanup: | ||||
Py_XDECREF(dict); | ||||
|
r16414 | Py_XDECREF(mod); | ||
} | ||||
static PyObject *index_getitem(indexObject *self, PyObject *value) | ||||
{ | ||||
char *node; | ||||
Py_ssize_t nodelen; | ||||
int rev; | ||||
if (PyInt_Check(value)) | ||||
return index_get(self, PyInt_AS_LONG(value)); | ||||
|
r16679 | if (node_check(value, &node, &nodelen) == -1) | ||
|
r16414 | return NULL; | ||
rev = index_find_node(self, node, nodelen); | ||||
if (rev >= -1) | ||||
return PyInt_FromLong(rev); | ||||
if (rev == -2) | ||||
raise_revlog_error(); | ||||
return NULL; | ||||
} | ||||
|
r16665 | static int nt_partialmatch(indexObject *self, const char *node, | ||
Py_ssize_t nodelen) | ||||
{ | ||||
int rev; | ||||
if (nt_init(self) == -1) | ||||
return -3; | ||||
if (self->ntrev > 0) { | ||||
/* ensure that the radix tree is fully populated */ | ||||
for (rev = self->ntrev - 1; rev >= 0; rev--) { | ||||
const char *n = index_node(self, rev); | ||||
if (n == NULL) | ||||
return -2; | ||||
if (nt_insert(self, n, rev) == -1) | ||||
return -3; | ||||
} | ||||
self->ntrev = rev; | ||||
} | ||||
return nt_find(self, node, nodelen, 1); | ||||
} | ||||
static PyObject *index_partialmatch(indexObject *self, PyObject *args) | ||||
{ | ||||
const char *fullnode; | ||||
int nodelen; | ||||
char *node; | ||||
int rev, i; | ||||
if (!PyArg_ParseTuple(args, "s#", &node, &nodelen)) | ||||
return NULL; | ||||
if (nodelen < 4) { | ||||
PyErr_SetString(PyExc_ValueError, "key too short"); | ||||
return NULL; | ||||
} | ||||
|
r17353 | if (nodelen > 40) { | ||
PyErr_SetString(PyExc_ValueError, "key too long"); | ||||
return NULL; | ||||
} | ||||
|
r16665 | |||
for (i = 0; i < nodelen; i++) | ||||
hexdigit(node, i); | ||||
if (PyErr_Occurred()) { | ||||
/* input contains non-hex characters */ | ||||
PyErr_Clear(); | ||||
Py_RETURN_NONE; | ||||
} | ||||
rev = nt_partialmatch(self, node, nodelen); | ||||
switch (rev) { | ||||
case -4: | ||||
raise_revlog_error(); | ||||
case -3: | ||||
return NULL; | ||||
case -2: | ||||
Py_RETURN_NONE; | ||||
case -1: | ||||
|
r30100 | return PyBytes_FromStringAndSize(nullid, 20); | ||
|
r16665 | } | ||
fullnode = index_node(self, rev); | ||||
if (fullnode == NULL) { | ||||
PyErr_Format(PyExc_IndexError, | ||||
"could not access rev %d", rev); | ||||
return NULL; | ||||
} | ||||
|
r30100 | return PyBytes_FromStringAndSize(fullnode, 20); | ||
|
r16665 | } | ||
|
r16414 | static PyObject *index_m_get(indexObject *self, PyObject *args) | ||
{ | ||||
|
r16679 | Py_ssize_t nodelen; | ||
PyObject *val; | ||||
|
r16414 | char *node; | ||
|
r16679 | int rev; | ||
|
r16414 | |||
|
r16679 | if (!PyArg_ParseTuple(args, "O", &val)) | ||
|
r16414 | return NULL; | ||
|
r16679 | if (node_check(val, &node, &nodelen) == -1) | ||
return NULL; | ||||
|
r16414 | rev = index_find_node(self, node, nodelen); | ||
|
r27638 | if (rev == -3) | ||
|
r16414 | return NULL; | ||
if (rev == -2) | ||||
Py_RETURN_NONE; | ||||
return PyInt_FromLong(rev); | ||||
} | ||||
static int index_contains(indexObject *self, PyObject *value) | ||||
{ | ||||
char *node; | ||||
Py_ssize_t nodelen; | ||||
if (PyInt_Check(value)) { | ||||
long rev = PyInt_AS_LONG(value); | ||||
return rev >= -1 && rev < index_length(self); | ||||
} | ||||
|
r16679 | if (node_check(value, &node, &nodelen) == -1) | ||
return -1; | ||||
|
r16414 | |||
switch (index_find_node(self, node, nodelen)) { | ||||
case -3: | ||||
return -1; | ||||
case -2: | ||||
return 0; | ||||
default: | ||||
return 1; | ||||
} | ||||
} | ||||
|
r18988 | typedef uint64_t bitmask; | ||
/* | ||||
* Given a disjoint set of revs, return all candidates for the | ||||
* greatest common ancestor. In revset notation, this is the set | ||||
* "heads(::a and ::b and ...)" | ||||
*/ | ||||
static PyObject *find_gca_candidates(indexObject *self, const int *revs, | ||||
int revcount) | ||||
{ | ||||
const bitmask allseen = (1ull << revcount) - 1; | ||||
const bitmask poison = 1ull << revcount; | ||||
PyObject *gca = PyList_New(0); | ||||
|
r20555 | int i, v, interesting; | ||
|
r18988 | int maxrev = -1; | ||
|
r22399 | bitmask sp; | ||
|
r18988 | bitmask *seen; | ||
|
r19727 | if (gca == NULL) | ||
return PyErr_NoMemory(); | ||||
|
r18988 | for (i = 0; i < revcount; i++) { | ||
if (revs[i] > maxrev) | ||||
maxrev = revs[i]; | ||||
} | ||||
seen = calloc(sizeof(*seen), maxrev + 1); | ||||
|
r19727 | if (seen == NULL) { | ||
Py_DECREF(gca); | ||||
|
r18988 | return PyErr_NoMemory(); | ||
|
r19727 | } | ||
|
r18988 | |||
for (i = 0; i < revcount; i++) | ||||
seen[revs[i]] = 1ull << i; | ||||
|
r20555 | interesting = revcount; | ||
|
r18988 | |||
for (v = maxrev; v >= 0 && interesting; v--) { | ||||
|
r22399 | bitmask sv = seen[v]; | ||
|
r18988 | int parents[2]; | ||
if (!sv) | ||||
continue; | ||||
if (sv < poison) { | ||||
interesting -= 1; | ||||
if (sv == allseen) { | ||||
PyObject *obj = PyInt_FromLong(v); | ||||
if (obj == NULL) | ||||
goto bail; | ||||
if (PyList_Append(gca, obj) == -1) { | ||||
Py_DECREF(obj); | ||||
goto bail; | ||||
} | ||||
sv |= poison; | ||||
for (i = 0; i < revcount; i++) { | ||||
|
r20555 | if (revs[i] == v) | ||
goto done; | ||||
|
r18988 | } | ||
} | ||||
} | ||||
|
r25810 | if (index_get_parents(self, v, parents, maxrev) < 0) | ||
goto bail; | ||||
|
r18988 | |||
for (i = 0; i < 2; i++) { | ||||
int p = parents[i]; | ||||
if (p == -1) | ||||
continue; | ||||
|
r19030 | sp = seen[p]; | ||
|
r18988 | if (sv < poison) { | ||
if (sp == 0) { | ||||
seen[p] = sv; | ||||
interesting++; | ||||
} | ||||
else if (sp != sv) | ||||
seen[p] |= sv; | ||||
} else { | ||||
if (sp && sp < poison) | ||||
interesting--; | ||||
seen[p] = sv; | ||||
} | ||||
} | ||||
} | ||||
done: | ||||
free(seen); | ||||
return gca; | ||||
bail: | ||||
free(seen); | ||||
Py_XDECREF(gca); | ||||
return NULL; | ||||
} | ||||
/* | ||||
* Given a disjoint set of revs, return the subset with the longest | ||||
* path to the root. | ||||
*/ | ||||
static PyObject *find_deepest(indexObject *self, PyObject *revs) | ||||
{ | ||||
const Py_ssize_t revcount = PyList_GET_SIZE(revs); | ||||
static const Py_ssize_t capacity = 24; | ||||
int *depth, *interesting = NULL; | ||||
int i, j, v, ninteresting; | ||||
|
r21730 | PyObject *dict = NULL, *keys = NULL; | ||
|
r18988 | long *seen = NULL; | ||
int maxrev = -1; | ||||
long final; | ||||
if (revcount > capacity) { | ||||
PyErr_Format(PyExc_OverflowError, | ||||
"bitset size (%ld) > capacity (%ld)", | ||||
|
r19062 | (long)revcount, (long)capacity); | ||
|
r18988 | return NULL; | ||
} | ||||
for (i = 0; i < revcount; i++) { | ||||
int n = (int)PyInt_AsLong(PyList_GET_ITEM(revs, i)); | ||||
if (n > maxrev) | ||||
maxrev = n; | ||||
} | ||||
depth = calloc(sizeof(*depth), maxrev + 1); | ||||
if (depth == NULL) | ||||
return PyErr_NoMemory(); | ||||
seen = calloc(sizeof(*seen), maxrev + 1); | ||||
if (seen == NULL) { | ||||
PyErr_NoMemory(); | ||||
goto bail; | ||||
} | ||||
interesting = calloc(sizeof(*interesting), 2 << revcount); | ||||
if (interesting == NULL) { | ||||
PyErr_NoMemory(); | ||||
goto bail; | ||||
} | ||||
|
r19502 | if (PyList_Sort(revs) == -1) | ||
goto bail; | ||||
|
r18988 | for (i = 0; i < revcount; i++) { | ||
int n = (int)PyInt_AsLong(PyList_GET_ITEM(revs, i)); | ||||
long b = 1l << i; | ||||
depth[n] = 1; | ||||
seen[n] = b; | ||||
interesting[b] = 1; | ||||
} | ||||
ninteresting = (int)revcount; | ||||
for (v = maxrev; v >= 0 && ninteresting > 1; v--) { | ||||
int dv = depth[v]; | ||||
int parents[2]; | ||||
long sv; | ||||
if (dv == 0) | ||||
continue; | ||||
sv = seen[v]; | ||||
|
r25810 | if (index_get_parents(self, v, parents, maxrev) < 0) | ||
goto bail; | ||||
|
r18988 | |||
for (i = 0; i < 2; i++) { | ||||
int p = parents[i]; | ||||
|
r27341 | long sp; | ||
|
r18988 | int dp; | ||
if (p == -1) | ||||
continue; | ||||
dp = depth[p]; | ||||
|
r27341 | sp = seen[p]; | ||
|
r18988 | if (dp <= dv) { | ||
depth[p] = dv + 1; | ||||
if (sp != sv) { | ||||
interesting[sv] += 1; | ||||
|
r27341 | seen[p] = sv; | ||
|
r18988 | if (sp) { | ||
interesting[sp] -= 1; | ||||
if (interesting[sp] == 0) | ||||
ninteresting -= 1; | ||||
} | ||||
} | ||||
} | ||||
else if (dv == dp - 1) { | ||||
|
r27341 | long nsp = sp | sv; | ||
|
r18988 | if (nsp == sp) | ||
continue; | ||||
seen[p] = nsp; | ||||
|
r19503 | interesting[sp] -= 1; | ||
if (interesting[sp] == 0 && interesting[nsp] > 0) | ||||
ninteresting -= 1; | ||||
|
r18988 | interesting[nsp] += 1; | ||
} | ||||
} | ||||
interesting[sv] -= 1; | ||||
if (interesting[sv] == 0) | ||||
ninteresting -= 1; | ||||
} | ||||
final = 0; | ||||
j = ninteresting; | ||||
for (i = 0; i < (int)(2 << revcount) && j > 0; i++) { | ||||
if (interesting[i] == 0) | ||||
continue; | ||||
final |= i; | ||||
j -= 1; | ||||
} | ||||
|
r21730 | if (final == 0) { | ||
keys = PyList_New(0); | ||||
goto bail; | ||||
} | ||||
|
r18988 | |||
dict = PyDict_New(); | ||||
if (dict == NULL) | ||||
goto bail; | ||||
|
r19504 | for (i = 0; i < revcount; i++) { | ||
|
r18988 | PyObject *key; | ||
if ((final & (1 << i)) == 0) | ||||
continue; | ||||
key = PyList_GET_ITEM(revs, i); | ||||
Py_INCREF(key); | ||||
Py_INCREF(Py_None); | ||||
if (PyDict_SetItem(dict, key, Py_None) == -1) { | ||||
Py_DECREF(key); | ||||
Py_DECREF(Py_None); | ||||
goto bail; | ||||
} | ||||
} | ||||
keys = PyDict_Keys(dict); | ||||
bail: | ||||
free(depth); | ||||
free(seen); | ||||
free(interesting); | ||||
Py_XDECREF(dict); | ||||
|
r21730 | return keys; | ||
|
r18988 | } | ||
/* | ||||
|
r21102 | * Given a (possibly overlapping) set of revs, return all the | ||
* common ancestors heads: heads(::args[0] and ::a[1] and ...) | ||||
*/ | ||||
static PyObject *index_commonancestorsheads(indexObject *self, PyObject *args) | ||||
{ | ||||
|
r21103 | PyObject *ret = NULL; | ||
|
r21102 | Py_ssize_t argcount, i, len; | ||
bitmask repeat = 0; | ||||
int revcount = 0; | ||||
int *revs; | ||||
argcount = PySequence_Length(args); | ||||
revs = malloc(argcount * sizeof(*revs)); | ||||
if (argcount > 0 && revs == NULL) | ||||
return PyErr_NoMemory(); | ||||
len = index_length(self) - 1; | ||||
for (i = 0; i < argcount; i++) { | ||||
static const int capacity = 24; | ||||
PyObject *obj = PySequence_GetItem(args, i); | ||||
bitmask x; | ||||
long val; | ||||
if (!PyInt_Check(obj)) { | ||||
PyErr_SetString(PyExc_TypeError, | ||||
"arguments must all be ints"); | ||||
|
r23945 | Py_DECREF(obj); | ||
|
r21102 | goto bail; | ||
} | ||||
val = PyInt_AsLong(obj); | ||||
|
r23945 | Py_DECREF(obj); | ||
|
r21102 | if (val == -1) { | ||
ret = PyList_New(0); | ||||
goto done; | ||||
} | ||||
if (val < 0 || val >= len) { | ||||
PyErr_SetString(PyExc_IndexError, | ||||
"index out of range"); | ||||
goto bail; | ||||
} | ||||
/* this cheesy bloom filter lets us avoid some more | ||||
* expensive duplicate checks in the common set-is-disjoint | ||||
* case */ | ||||
x = 1ull << (val & 0x3f); | ||||
if (repeat & x) { | ||||
int k; | ||||
for (k = 0; k < revcount; k++) { | ||||
if (val == revs[k]) | ||||
goto duplicate; | ||||
} | ||||
} | ||||
else repeat |= x; | ||||
if (revcount >= capacity) { | ||||
PyErr_Format(PyExc_OverflowError, | ||||
"bitset size (%d) > capacity (%d)", | ||||
revcount, capacity); | ||||
goto bail; | ||||
} | ||||
revs[revcount++] = (int)val; | ||||
duplicate:; | ||||
} | ||||
if (revcount == 0) { | ||||
ret = PyList_New(0); | ||||
goto done; | ||||
} | ||||
if (revcount == 1) { | ||||
PyObject *obj; | ||||
ret = PyList_New(1); | ||||
if (ret == NULL) | ||||
goto bail; | ||||
obj = PyInt_FromLong(revs[0]); | ||||
if (obj == NULL) | ||||
goto bail; | ||||
PyList_SET_ITEM(ret, 0, obj); | ||||
goto done; | ||||
} | ||||
|
r21103 | ret = find_gca_candidates(self, revs, revcount); | ||
if (ret == NULL) | ||||
|
r21102 | goto bail; | ||
done: | ||||
free(revs); | ||||
return ret; | ||||
bail: | ||||
free(revs); | ||||
Py_XDECREF(ret); | ||||
return NULL; | ||||
} | ||||
/* | ||||
|
r24004 | * Given a (possibly overlapping) set of revs, return the greatest | ||
* common ancestors: those with the longest path to the root. | ||||
*/ | ||||
static PyObject *index_ancestors(indexObject *self, PyObject *args) | ||||
{ | ||||
|
r26048 | PyObject *ret; | ||
|
r24004 | PyObject *gca = index_commonancestorsheads(self, args); | ||
if (gca == NULL) | ||||
return NULL; | ||||
if (PyList_GET_SIZE(gca) <= 1) { | ||||
return gca; | ||||
} | ||||
|
r26048 | ret = find_deepest(self, gca); | ||
Py_DECREF(gca); | ||||
return ret; | ||||
|
r24004 | } | ||
/* | ||||
|
r16414 | * Invalidate any trie entries introduced by added revs. | ||
*/ | ||||
static void nt_invalidate_added(indexObject *self, Py_ssize_t start) | ||||
{ | ||||
Py_ssize_t i, len = PyList_GET_SIZE(self->added); | ||||
for (i = start; i < len; i++) { | ||||
PyObject *tuple = PyList_GET_ITEM(self->added, i); | ||||
PyObject *node = PyTuple_GET_ITEM(tuple, 7); | ||||
|
r30100 | nt_insert(self, PyBytes_AS_STRING(node), -1); | ||
|
r16414 | } | ||
|
r16732 | if (start == 0) | ||
Py_CLEAR(self->added); | ||||
|
r16414 | } | ||
/* | ||||
* Delete a numeric range of revs, which must be at the end of the | ||||
* range, but exclude the sentinel nullid entry. | ||||
*/ | ||||
static int index_slice_del(indexObject *self, PyObject *item) | ||||
|
r16363 | { | ||
Py_ssize_t start, stop, step, slicelength; | ||||
Py_ssize_t length = index_length(self); | ||||
|
r16784 | int ret = 0; | ||
|
r16363 | |||
|
r30171 | /* Argument changed from PySliceObject* to PyObject* in Python 3. */ | ||
#ifdef IS_PY3K | ||||
if (PySlice_GetIndicesEx(item, length, | ||||
#else | ||||
|
r16363 | if (PySlice_GetIndicesEx((PySliceObject*)item, length, | ||
|
r30171 | #endif | ||
|
r16363 | &start, &stop, &step, &slicelength) < 0) | ||
return -1; | ||||
if (slicelength <= 0) | ||||
return 0; | ||||
if ((step < 0 && start < stop) || (step > 0 && start > stop)) | ||||
stop = start; | ||||
|
r7108 | |||
|
r16363 | if (step < 0) { | ||
stop = start + 1; | ||||
start = stop + step*(slicelength - 1) - 1; | ||||
step = -step; | ||||
} | ||||
if (step != 1) { | ||||
PyErr_SetString(PyExc_ValueError, | ||||
"revlog index delete requires step size of 1"); | ||||
return -1; | ||||
|
r7108 | } | ||
|
r16363 | |||
if (stop != length - 1) { | ||||
PyErr_SetString(PyExc_IndexError, | ||||
"revlog index deletion indices are invalid"); | ||||
return -1; | ||||
} | ||||
|
r16414 | if (start < self->length - 1) { | ||
if (self->nt) { | ||||
Py_ssize_t i; | ||||
for (i = start + 1; i < self->length - 1; i++) { | ||||
const char *node = index_node(self, i); | ||||
if (node) | ||||
nt_insert(self, node, -1); | ||||
} | ||||
if (self->added) | ||||
nt_invalidate_added(self, 0); | ||||
if (self->ntrev > start) | ||||
self->ntrev = (int)start; | ||||
} | ||||
|
r16363 | self->length = start + 1; | ||
|
r18504 | if (start < self->raw_length) { | ||
if (self->cache) { | ||||
Py_ssize_t i; | ||||
for (i = start; i < self->raw_length; i++) | ||||
Py_CLEAR(self->cache[i]); | ||||
} | ||||
|
r16784 | self->raw_length = start; | ||
|
r18504 | } | ||
|
r16784 | goto done; | ||
|
r7108 | } | ||
|
r16414 | if (self->nt) { | ||
nt_invalidate_added(self, start - self->length + 1); | ||||
if (self->ntrev > start) | ||||
self->ntrev = (int)start; | ||||
} | ||||
|
r16784 | if (self->added) | ||
ret = PyList_SetSlice(self->added, start - self->length + 1, | ||||
PyList_GET_SIZE(self->added), NULL); | ||||
done: | ||||
|
r16787 | Py_CLEAR(self->headrevs); | ||
|
r16784 | return ret; | ||
|
r16363 | } | ||
|
r16414 | /* | ||
* Supported ops: | ||||
* | ||||
* slice deletion | ||||
* string assignment (extend node->rev mapping) | ||||
* string deletion (shrink node->rev mapping) | ||||
*/ | ||||
static int index_assign_subscript(indexObject *self, PyObject *item, | ||||
PyObject *value) | ||||
{ | ||||
char *node; | ||||
Py_ssize_t nodelen; | ||||
long rev; | ||||
if (PySlice_Check(item) && value == NULL) | ||||
return index_slice_del(self, item); | ||||
if (node_check(item, &node, &nodelen) == -1) | ||||
return -1; | ||||
if (value == NULL) | ||||
return self->nt ? nt_insert(self, node, -1) : 0; | ||||
rev = PyInt_AsLong(value); | ||||
if (rev > INT_MAX || rev < 0) { | ||||
if (!PyErr_Occurred()) | ||||
PyErr_SetString(PyExc_ValueError, "rev out of range"); | ||||
return -1; | ||||
} | ||||
|
r23468 | |||
if (nt_init(self) == -1) | ||||
return -1; | ||||
|
r16414 | return nt_insert(self, node, (int)rev); | ||
} | ||||
/* | ||||
* Find all RevlogNG entries in an index that has inline data. Update | ||||
* the optional "offsets" table with those entries. | ||||
*/ | ||||
|
r22401 | static Py_ssize_t inline_scan(indexObject *self, const char **offsets) | ||
|
r16363 | { | ||
|
r30100 | const char *data = PyBytes_AS_STRING(self->data); | ||
|
r20167 | Py_ssize_t pos = 0; | ||
|
r30100 | Py_ssize_t end = PyBytes_GET_SIZE(self->data); | ||
|
r16863 | long incr = v1_hdrsize; | ||
|
r16363 | Py_ssize_t len = 0; | ||
|
r13254 | |||
|
r20167 | while (pos + v1_hdrsize <= end && pos >= 0) { | ||
|
r16363 | uint32_t comp_len; | ||
/* 3rd element of header is length of compressed inline data */ | ||||
|
r20167 | comp_len = getbe32(data + pos + 8); | ||
|
r16863 | incr = v1_hdrsize + comp_len; | ||
|
r16363 | if (offsets) | ||
|
r20167 | offsets[len] = data + pos; | ||
|
r16363 | len++; | ||
|
r20167 | pos += incr; | ||
|
r16363 | } | ||
|
r13254 | |||
|
r20167 | if (pos != end) { | ||
|
r16363 | if (!PyErr_Occurred()) | ||
PyErr_SetString(PyExc_ValueError, "corrupt index file"); | ||||
return -1; | ||||
} | ||||
return len; | ||||
} | ||||
|
r13254 | |||
|
r16572 | static int index_init(indexObject *self, PyObject *args) | ||
|
r16363 | { | ||
|
r16572 | PyObject *data_obj, *inlined_obj; | ||
Py_ssize_t size; | ||||
|
r20109 | /* Initialize before argument-checking to avoid index_dealloc() crash. */ | ||
self->raw_length = 0; | ||||
self->added = NULL; | ||||
self->cache = NULL; | ||||
self->data = NULL; | ||||
self->headrevs = NULL; | ||||
|
r22484 | self->filteredrevs = Py_None; | ||
Py_INCREF(Py_None); | ||||
|
r20109 | self->nt = NULL; | ||
self->offsets = NULL; | ||||
|
r16572 | if (!PyArg_ParseTuple(args, "OO", &data_obj, &inlined_obj)) | ||
return -1; | ||||
|
r30100 | if (!PyBytes_Check(data_obj)) { | ||
|
r16572 | PyErr_SetString(PyExc_TypeError, "data is not a string"); | ||
return -1; | ||||
} | ||||
|
r30100 | size = PyBytes_GET_SIZE(data_obj); | ||
|
r16572 | |||
|
r16363 | self->inlined = inlined_obj && PyObject_IsTrue(inlined_obj); | ||
self->data = data_obj; | ||||
|
r16414 | self->ntlength = self->ntcapacity = 0; | ||
self->ntdepth = self->ntsplits = 0; | ||||
self->ntlookups = self->ntmisses = 0; | ||||
self->ntrev = -1; | ||||
|
r16597 | Py_INCREF(self->data); | ||
|
r7108 | |||
|
r16363 | if (self->inlined) { | ||
|
r22401 | Py_ssize_t len = inline_scan(self, NULL); | ||
|
r16363 | if (len == -1) | ||
goto bail; | ||||
self->raw_length = len; | ||||
self->length = len + 1; | ||||
} else { | ||||
|
r16863 | if (size % v1_hdrsize) { | ||
|
r16363 | PyErr_SetString(PyExc_ValueError, "corrupt index file"); | ||
goto bail; | ||||
} | ||||
|
r16863 | self->raw_length = size / v1_hdrsize; | ||
|
r16363 | self->length = self->raw_length + 1; | ||
} | ||||
return 0; | ||||
bail: | ||||
return -1; | ||||
} | ||||
|
r16414 | static PyObject *index_nodemap(indexObject *self) | ||
{ | ||||
|
r16572 | Py_INCREF(self); | ||
|
r16414 | return (PyObject *)self; | ||
} | ||||
|
r16363 | static void index_dealloc(indexObject *self) | ||
{ | ||||
|
r16370 | _index_clearcaches(self); | ||
|
r22484 | Py_XDECREF(self->filteredrevs); | ||
|
r20109 | Py_XDECREF(self->data); | ||
|
r16363 | Py_XDECREF(self->added); | ||
PyObject_Del(self); | ||||
} | ||||
static PySequenceMethods index_sequence_methods = { | ||||
(lenfunc)index_length, /* sq_length */ | ||||
0, /* sq_concat */ | ||||
0, /* sq_repeat */ | ||||
(ssizeargfunc)index_get, /* sq_item */ | ||||
|
r16414 | 0, /* sq_slice */ | ||
0, /* sq_ass_item */ | ||||
0, /* sq_ass_slice */ | ||||
(objobjproc)index_contains, /* sq_contains */ | ||||
|
r16363 | }; | ||
static PyMappingMethods index_mapping_methods = { | ||||
(lenfunc)index_length, /* mp_length */ | ||||
|
r16414 | (binaryfunc)index_getitem, /* mp_subscript */ | ||
|
r16363 | (objobjargproc)index_assign_subscript, /* mp_ass_subscript */ | ||
}; | ||||
static PyMethodDef index_methods[] = { | ||||
|
r18988 | {"ancestors", (PyCFunction)index_ancestors, METH_VARARGS, | ||
"return the gca set of the given revs"}, | ||||
|
r21102 | {"commonancestorsheads", (PyCFunction)index_commonancestorsheads, | ||
METH_VARARGS, | ||||
"return the heads of the common ancestors of the given revs"}, | ||||
|
r16370 | {"clearcaches", (PyCFunction)index_clearcaches, METH_NOARGS, | ||
"clear the index caches"}, | ||||
|
r16414 | {"get", (PyCFunction)index_m_get, METH_VARARGS, | ||
"get an index entry"}, | ||||
|
r25190 | {"computephasesmapsets", (PyCFunction)compute_phases_map_sets, | ||
METH_VARARGS, "compute phases"}, | ||||
|
r26053 | {"reachableroots2", (PyCFunction)reachableroots2, METH_VARARGS, | ||
|
r26004 | "reachableroots"}, | ||
|
r22484 | {"headrevs", (PyCFunction)index_headrevs, METH_VARARGS, | ||
|
r23087 | "get head revisions"}, /* Can do filtering since 3.2 */ | ||
{"headrevsfiltered", (PyCFunction)index_headrevs, METH_VARARGS, | ||||
"get filtered head revisions"}, /* Can always do filtering */ | ||||
|
r16363 | {"insert", (PyCFunction)index_insert, METH_VARARGS, | ||
"insert an index entry"}, | ||||
|
r16665 | {"partialmatch", (PyCFunction)index_partialmatch, METH_VARARGS, | ||
"match a potentially ambiguous node ID"}, | ||||
|
r16414 | {"stats", (PyCFunction)index_stats, METH_NOARGS, | ||
"stats for the index"}, | ||||
{NULL} /* Sentinel */ | ||||
}; | ||||
static PyGetSetDef index_getset[] = { | ||||
{"nodemap", (getter)index_nodemap, NULL, "nodemap", NULL}, | ||||
|
r16363 | {NULL} /* Sentinel */ | ||
}; | ||||
static PyTypeObject indexType = { | ||||
|
r30103 | PyVarObject_HEAD_INIT(NULL, 0) | ||
|
r16363 | "parsers.index", /* tp_name */ | ||
sizeof(indexObject), /* tp_basicsize */ | ||||
0, /* tp_itemsize */ | ||||
(destructor)index_dealloc, /* tp_dealloc */ | ||||
0, /* tp_print */ | ||||
0, /* tp_getattr */ | ||||
0, /* tp_setattr */ | ||||
0, /* tp_compare */ | ||||
0, /* tp_repr */ | ||||
0, /* tp_as_number */ | ||||
&index_sequence_methods, /* tp_as_sequence */ | ||||
&index_mapping_methods, /* 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 */ | ||||
"revlog index", /* tp_doc */ | ||||
0, /* tp_traverse */ | ||||
0, /* tp_clear */ | ||||
0, /* tp_richcompare */ | ||||
0, /* tp_weaklistoffset */ | ||||
0, /* tp_iter */ | ||||
0, /* tp_iternext */ | ||||
index_methods, /* tp_methods */ | ||||
0, /* tp_members */ | ||||
|
r16414 | index_getset, /* tp_getset */ | ||
|
r16363 | 0, /* tp_base */ | ||
0, /* tp_dict */ | ||||
0, /* tp_descr_get */ | ||||
0, /* tp_descr_set */ | ||||
0, /* tp_dictoffset */ | ||||
(initproc)index_init, /* tp_init */ | ||||
0, /* tp_alloc */ | ||||
}; | ||||
/* | ||||
|
r16414 | * returns a tuple of the form (index, index, cache) with elements as | ||
|
r16363 | * follows: | ||
|
r7108 | * | ||
|
r16414 | * index: an index object that lazily parses RevlogNG records | ||
|
r16363 | * cache: if data is inlined, a tuple (index_file_content, 0), else None | ||
* | ||||
* added complications are for backwards compatibility | ||||
|
r7108 | */ | ||
|
r13254 | static PyObject *parse_index2(PyObject *self, PyObject *args) | ||
|
r7108 | { | ||
|
r16572 | PyObject *tuple = NULL, *cache = NULL; | ||
|
r16363 | indexObject *idx; | ||
|
r16572 | int ret; | ||
|
r16363 | |||
idx = PyObject_New(indexObject, &indexType); | ||||
if (idx == NULL) | ||||
goto bail; | ||||
|
r7108 | |||
|
r16572 | ret = index_init(idx, args); | ||
if (ret == -1) | ||||
|
r16363 | goto bail; | ||
|
r7108 | |||
|
r16363 | if (idx->inlined) { | ||
cache = Py_BuildValue("iO", 0, idx->data); | ||||
if (cache == NULL) | ||||
goto bail; | ||||
|
r7108 | } else { | ||
cache = Py_None; | ||||
|
r16363 | Py_INCREF(cache); | ||
|
r7108 | } | ||
|
r16363 | tuple = Py_BuildValue("NN", idx, cache); | ||
if (!tuple) | ||||
goto bail; | ||||
return tuple; | ||||
|
r7108 | |||
|
r16363 | bail: | ||
Py_XDECREF(idx); | ||||
|
r7108 | Py_XDECREF(cache); | ||
|
r16363 | Py_XDECREF(tuple); | ||
|
r7108 | return NULL; | ||
} | ||||
|
r24017 | #define BUMPED_FIX 1 | ||
#define USING_SHA_256 2 | ||||
|
r26591 | #define FM1_HEADER_SIZE (4 + 8 + 2 + 2 + 1 + 1 + 1) | ||
|
r24017 | |||
static PyObject *readshas( | ||||
const char *source, unsigned char num, Py_ssize_t hashwidth) | ||||
{ | ||||
int i; | ||||
PyObject *list = PyTuple_New(num); | ||||
if (list == NULL) { | ||||
return NULL; | ||||
} | ||||
for (i = 0; i < num; i++) { | ||||
|
r30100 | PyObject *hash = PyBytes_FromStringAndSize(source, hashwidth); | ||
|
r24017 | if (hash == NULL) { | ||
Py_DECREF(list); | ||||
return NULL; | ||||
} | ||||
|
r26213 | PyTuple_SET_ITEM(list, i, hash); | ||
|
r24017 | source += hashwidth; | ||
} | ||||
return list; | ||||
} | ||||
|
r26591 | static PyObject *fm1readmarker(const char *databegin, const char *dataend, | ||
uint32_t *msize) | ||||
|
r24017 | { | ||
|
r26591 | const char *data = databegin; | ||
|
r24017 | 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; | ||||
|
r26591 | if (data + FM1_HEADER_SIZE > dataend) { | ||
goto overflow; | ||||
} | ||||
|
r24019 | *msize = getbe32(data); | ||
|
r24017 | 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++); | ||||
|
r26591 | if (databegin + *msize > dataend) { | ||
goto overflow; | ||||
} | ||||
dataend = databegin + *msize; /* narrow down to marker size */ | ||||
if (data + hashwidth > dataend) { | ||||
goto overflow; | ||||
} | ||||
|
r30100 | prec = PyBytes_FromStringAndSize(data, hashwidth); | ||
|
r24017 | data += hashwidth; | ||
if (prec == NULL) { | ||||
goto bail; | ||||
} | ||||
|
r26591 | if (data + nsuccs * hashwidth > dataend) { | ||
goto overflow; | ||||
} | ||||
|
r24017 | succs = readshas(data, nsuccs, hashwidth); | ||
if (succs == NULL) { | ||||
goto bail; | ||||
} | ||||
data += nsuccs * hashwidth; | ||||
if (nparents == 1 || nparents == 2) { | ||||
|
r26591 | if (data + nparents * hashwidth > dataend) { | ||
goto overflow; | ||||
} | ||||
|
r24017 | parents = readshas(data, nparents, hashwidth); | ||
if (parents == NULL) { | ||||
goto bail; | ||||
} | ||||
data += nparents * hashwidth; | ||||
} else { | ||||
parents = Py_None; | ||||
} | ||||
|
r26591 | if (data + 2 * nmetadata > dataend) { | ||
goto overflow; | ||||
} | ||||
|
r24017 | 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; | ||||
|
r26590 | Py_ssize_t leftsize = (unsigned char)(*data++); | ||
Py_ssize_t rightsize = (unsigned char)(*data++); | ||||
|
r26591 | if (meta + leftsize + rightsize > dataend) { | ||
goto overflow; | ||||
} | ||||
|
r30100 | left = PyBytes_FromStringAndSize(meta, leftsize); | ||
|
r26590 | meta += leftsize; | ||
|
r30100 | right = PyBytes_FromStringAndSize(meta, rightsize); | ||
|
r26590 | meta += rightsize; | ||
|
r26214 | tmp = PyTuple_New(2); | ||
if (!left || !right || !tmp) { | ||||
|
r24017 | Py_XDECREF(left); | ||
Py_XDECREF(right); | ||||
|
r26214 | Py_XDECREF(tmp); | ||
|
r24017 | goto bail; | ||
} | ||||
|
r26214 | PyTuple_SET_ITEM(tmp, 0, left); | ||
PyTuple_SET_ITEM(tmp, 1, right); | ||||
|
r26213 | PyTuple_SET_ITEM(metadata, i, tmp); | ||
|
r24017 | } | ||
|
r24019 | ret = Py_BuildValue("(OOHO(di)O)", prec, succs, flags, | ||
|
r24017 | metadata, mtime, (int)tz * 60, parents); | ||
|
r26591 | goto bail; /* return successfully */ | ||
overflow: | ||||
PyErr_SetString(PyExc_ValueError, "overflow in obsstore"); | ||||
|
r24017 | bail: | ||
Py_XDECREF(prec); | ||||
Py_XDECREF(succs); | ||||
Py_XDECREF(metadata); | ||||
if (parents != Py_None) | ||||
Py_XDECREF(parents); | ||||
return ret; | ||||
} | ||||
|
r24019 | |||
static PyObject *fm1readmarkers(PyObject *self, PyObject *args) { | ||||
|
r26591 | const char *data, *dataend; | ||
|
r26872 | int datalen; | ||
|
r26107 | Py_ssize_t offset, stop; | ||
|
r24019 | PyObject *markers = NULL; | ||
|
r26107 | if (!PyArg_ParseTuple(args, "s#nn", &data, &datalen, &offset, &stop)) { | ||
|
r24019 | return NULL; | ||
} | ||||
|
r26591 | dataend = data + datalen; | ||
|
r24019 | data += offset; | ||
markers = PyList_New(0); | ||||
if (!markers) { | ||||
return NULL; | ||||
} | ||||
while (offset < stop) { | ||||
uint32_t msize; | ||||
int error; | ||||
|
r26591 | PyObject *record = fm1readmarker(data, dataend, &msize); | ||
|
r24019 | 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; | ||||
} | ||||
|
r6389 | static char parsers_doc[] = "Efficient content parsing."; | ||
|
r17606 | PyObject *encodedir(PyObject *self, PyObject *args); | ||
|
r17616 | PyObject *pathencode(PyObject *self, PyObject *args); | ||
|
r18430 | PyObject *lowerencode(PyObject *self, PyObject *args); | ||
|
r17606 | |||
|
r6389 | static PyMethodDef methods[] = { | ||
|
r16955 | {"pack_dirstate", pack_dirstate, METH_VARARGS, "pack a dirstate\n"}, | ||
|
r27592 | {"nonnormalentries", nonnormalentries, METH_VARARGS, | ||
"create a set containing non-normal entries of given dirstate\n"}, | ||||
|
r6389 | {"parse_manifest", parse_manifest, METH_VARARGS, "parse a manifest\n"}, | ||
|
r7093 | {"parse_dirstate", parse_dirstate, METH_VARARGS, "parse a dirstate\n"}, | ||
|
r13254 | {"parse_index2", parse_index2, METH_VARARGS, "parse a revlog index\n"}, | ||
|
r22778 | {"asciilower", asciilower, METH_VARARGS, "lowercase an ASCII string\n"}, | ||
|
r24577 | {"asciiupper", asciiupper, METH_VARARGS, "uppercase an ASCII string\n"}, | ||
|
r25584 | {"dict_new_presized", dict_new_presized, METH_VARARGS, | ||
"construct a dict with an expected size\n"}, | ||||
|
r24609 | {"make_file_foldmap", make_file_foldmap, METH_VARARGS, | ||
"make file foldmap\n"}, | ||||
|
r17606 | {"encodedir", encodedir, METH_VARARGS, "encodedir a path\n"}, | ||
|
r17616 | {"pathencode", pathencode, METH_VARARGS, "fncache-encode a path\n"}, | ||
|
r18430 | {"lowerencode", lowerencode, METH_VARARGS, "lower-encode a path\n"}, | ||
|
r24019 | {"fm1readmarkers", fm1readmarkers, METH_VARARGS, | ||
"parse v1 obsolete markers\n"}, | ||||
|
r6389 | {NULL, NULL} | ||
}; | ||||
|
r18900 | void dirs_module_init(PyObject *mod); | ||
|
r24214 | void manifest_module_init(PyObject *mod); | ||
|
r18900 | |||
|
r16363 | static void module_init(PyObject *mod) | ||
{ | ||||
|
r20742 | /* 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); | ||||
|
r18900 | dirs_module_init(mod); | ||
|
r24214 | manifest_module_init(mod); | ||
|
r18900 | |||
|
r16604 | indexType.tp_new = PyType_GenericNew; | ||
|
r21809 | if (PyType_Ready(&indexType) < 0 || | ||
PyType_Ready(&dirstateTupleType) < 0) | ||||
|
r16363 | return; | ||
Py_INCREF(&indexType); | ||||
PyModule_AddObject(mod, "index", (PyObject *)&indexType); | ||||
|
r21809 | Py_INCREF(&dirstateTupleType); | ||
PyModule_AddObject(mod, "dirstatetuple", | ||||
(PyObject *)&dirstateTupleType); | ||||
|
r16363 | |||
nullentry = Py_BuildValue("iiiiiiis#", 0, 0, 0, | ||||
-1, -1, -1, -1, nullid, 20); | ||||
if (nullentry) | ||||
PyObject_GC_UnTrack(nullentry); | ||||
} | ||||
|
r20742 | static int check_python_version(void) | ||
{ | ||||
|
r23943 | PyObject *sys = PyImport_ImportModule("sys"), *ver; | ||
long hexversion; | ||||
if (!sys) | ||||
return -1; | ||||
ver = PyObject_GetAttrString(sys, "hexversion"); | ||||
Py_DECREF(sys); | ||||
if (!ver) | ||||
return -1; | ||||
hexversion = PyInt_AsLong(ver); | ||||
Py_DECREF(ver); | ||||
|
r20742 | /* 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)) { | ||||
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()); | ||||
return -1; | ||||
} | ||||
return 0; | ||||
} | ||||
|
r11361 | #ifdef IS_PY3K | ||
static struct PyModuleDef parsers_module = { | ||||
PyModuleDef_HEAD_INIT, | ||||
"parsers", | ||||
parsers_doc, | ||||
-1, | ||||
methods | ||||
}; | ||||
PyMODINIT_FUNC PyInit_parsers(void) | ||||
{ | ||||
|
r20797 | PyObject *mod; | ||
|
r20742 | if (check_python_version() == -1) | ||
|
r30090 | return NULL; | ||
|
r20797 | mod = PyModule_Create(&parsers_module); | ||
|
r16363 | module_init(mod); | ||
return mod; | ||||
|
r11361 | } | ||
#else | ||||
|
r6389 | PyMODINIT_FUNC initparsers(void) | ||
{ | ||||
|
r20797 | PyObject *mod; | ||
|
r20742 | if (check_python_version() == -1) | ||
return; | ||||
|
r20797 | mod = Py_InitModule3("parsers", methods, parsers_doc); | ||
|
r16363 | module_init(mod); | ||
|
r6389 | } | ||
|
r11361 | #endif | ||