comparison mercurial/parsers.c @ 26053:b68c9d232db6

reachableroots: use internal "revstates" array to test if rev is a root The main goal of this patch series is to reduce the use of PyXxx() function that is likely to require ugly error handling and inc/decref. Plus, this is faster than using PySet_Contains(). revset #0: 0::tip 0) 0.004168 1) 0.003678 88% This patch ignores out-of-range roots as they are in the pure implementation. Because reachable sets are calculated from heads, and out-of-range heads raise IndexError, we can just take out-of-range roots as unreachable. Otherwise, the test of "hg log -Gr '. + wdir()'" would fail. "heads" argument is changed to a list. Should we have to rename the C function as its signature is changed?
author Yuya Nishihara <yuya@tcha.org>
date Fri, 14 Aug 2015 15:43:29 +0900
parents b970418bbafe
children 5049e10fed14
comparison
equal deleted inserted replaced
26052:b970418bbafe 26053:b68c9d232db6
1106 phases[i] = phases[parent_1]; 1106 phases[i] = phases[parent_1];
1107 if (parent_2 >= 0 && phases[parent_2] > phases[i]) 1107 if (parent_2 >= 0 && phases[parent_2] > phases[i])
1108 phases[i] = phases[parent_2]; 1108 phases[i] = phases[parent_2];
1109 } 1109 }
1110 1110
1111 static PyObject *reachableroots(indexObject *self, PyObject *args) 1111 static PyObject *reachableroots2(indexObject *self, PyObject *args)
1112 { 1112 {
1113 1113
1114 /* Input */ 1114 /* Input */
1115 long minroot; 1115 long minroot;
1116 PyObject *includepatharg = NULL; 1116 PyObject *includepatharg = NULL;
1117 int includepath = 0; 1117 int includepath = 0;
1118 /* heads is a list */ 1118 /* heads and roots are lists */
1119 PyObject *heads = NULL; 1119 PyObject *heads = NULL;
1120 /* roots is a set */
1121 PyObject *roots = NULL; 1120 PyObject *roots = NULL;
1122 PyObject *reachable = NULL; 1121 PyObject *reachable = NULL;
1123 1122
1124 PyObject *val; 1123 PyObject *val;
1125 Py_ssize_t len = index_length(self) - 1; 1124 Py_ssize_t len = index_length(self) - 1;
1134 /* Internal data structure: 1133 /* Internal data structure:
1135 * tovisit: array of length len+1 (all revs + nullrev), filled upto lentovisit 1134 * tovisit: array of length len+1 (all revs + nullrev), filled upto lentovisit
1136 * revstates: array of length len+1 (all revs + nullrev) */ 1135 * revstates: array of length len+1 (all revs + nullrev) */
1137 int *tovisit = NULL; 1136 int *tovisit = NULL;
1138 long lentovisit = 0; 1137 long lentovisit = 0;
1139 enum { RS_SEEN = 1 }; 1138 enum { RS_SEEN = 1, RS_ROOT = 2 };
1140 char *revstates = NULL; 1139 char *revstates = NULL;
1141 1140
1142 /* Get arguments */ 1141 /* Get arguments */
1143 if (!PyArg_ParseTuple(args, "lO!O!O!", &minroot, &PyList_Type, &heads, 1142 if (!PyArg_ParseTuple(args, "lO!O!O!", &minroot, &PyList_Type, &heads,
1144 &PySet_Type, &roots, &PyBool_Type, &includepatharg)) 1143 &PyList_Type, &roots,
1144 &PyBool_Type, &includepatharg))
1145 goto bail; 1145 goto bail;
1146 1146
1147 if (includepatharg == Py_True) 1147 if (includepatharg == Py_True)
1148 includepath = 1; 1148 includepath = 1;
1149 1149
1165 if (revstates == NULL) { 1165 if (revstates == NULL) {
1166 PyErr_NoMemory(); 1166 PyErr_NoMemory();
1167 goto bail; 1167 goto bail;
1168 } 1168 }
1169 1169
1170 l = PyList_GET_SIZE(roots);
1171 for (i = 0; i < l; i++) {
1172 revnum = PyInt_AsLong(PyList_GET_ITEM(roots, i));
1173 if (revnum == -1 && PyErr_Occurred())
1174 goto bail;
1175 /* If root is out of range, e.g. wdir(), it must be unreachable
1176 * from heads. So we can just ignore it. */
1177 if (revnum + 1 < 0 || revnum + 1 >= len + 1)
1178 continue;
1179 revstates[revnum + 1] |= RS_ROOT;
1180 }
1181
1170 /* Populate tovisit with all the heads */ 1182 /* Populate tovisit with all the heads */
1171 l = PyList_GET_SIZE(heads); 1183 l = PyList_GET_SIZE(heads);
1172 for (i = 0; i < l; i++) { 1184 for (i = 0; i < l; i++) {
1173 revnum = PyInt_AsLong(PyList_GET_ITEM(heads, i)); 1185 revnum = PyInt_AsLong(PyList_GET_ITEM(heads, i));
1174 if (revnum == -1 && PyErr_Occurred()) 1186 if (revnum == -1 && PyErr_Occurred())
1186 /* Visit the tovisit list and find the reachable roots */ 1198 /* Visit the tovisit list and find the reachable roots */
1187 k = 0; 1199 k = 0;
1188 while (k < lentovisit) { 1200 while (k < lentovisit) {
1189 /* Add the node to reachable if it is a root*/ 1201 /* Add the node to reachable if it is a root*/
1190 revnum = tovisit[k++]; 1202 revnum = tovisit[k++];
1191 val = PyInt_FromLong(revnum); 1203 if (revstates[revnum + 1] & RS_ROOT) {
1192 if (val == NULL) 1204 val = PyInt_FromLong(revnum);
1193 goto bail; 1205 if (val == NULL)
1194 if (PySet_Contains(roots, val) == 1) { 1206 goto bail;
1195 PySet_Add(reachable, val); 1207 PySet_Add(reachable, val);
1196 if (includepath == 0) { 1208 Py_DECREF(val);
1197 Py_DECREF(val); 1209 if (includepath == 0)
1198 continue; 1210 continue;
1199 } 1211 }
1200 }
1201 Py_DECREF(val);
1202 1212
1203 /* Add its parents to the list of nodes to visit */ 1213 /* Add its parents to the list of nodes to visit */
1204 if (revnum == -1) 1214 if (revnum == -1)
1205 continue; 1215 continue;
1206 r = index_get_parents(self, revnum, parents, (int)len - 1); 1216 r = index_get_parents(self, revnum, parents, (int)len - 1);
2432 "clear the index caches"}, 2442 "clear the index caches"},
2433 {"get", (PyCFunction)index_m_get, METH_VARARGS, 2443 {"get", (PyCFunction)index_m_get, METH_VARARGS,
2434 "get an index entry"}, 2444 "get an index entry"},
2435 {"computephasesmapsets", (PyCFunction)compute_phases_map_sets, 2445 {"computephasesmapsets", (PyCFunction)compute_phases_map_sets,
2436 METH_VARARGS, "compute phases"}, 2446 METH_VARARGS, "compute phases"},
2437 {"reachableroots", (PyCFunction)reachableroots, METH_VARARGS, 2447 {"reachableroots2", (PyCFunction)reachableroots2, METH_VARARGS,
2438 "reachableroots"}, 2448 "reachableroots"},
2439 {"headrevs", (PyCFunction)index_headrevs, METH_VARARGS, 2449 {"headrevs", (PyCFunction)index_headrevs, METH_VARARGS,
2440 "get head revisions"}, /* Can do filtering since 3.2 */ 2450 "get head revisions"}, /* Can do filtering since 3.2 */
2441 {"headrevsfiltered", (PyCFunction)index_headrevs, METH_VARARGS, 2451 {"headrevsfiltered", (PyCFunction)index_headrevs, METH_VARARGS,
2442 "get filtered head revisions"}, /* Can always do filtering */ 2452 "get filtered head revisions"}, /* Can always do filtering */