Show More
@@ -1332,6 +1332,197 b' bail:' | |||||
1332 | return NULL; |
|
1332 | return NULL; | |
1333 | } |
|
1333 | } | |
1334 |
|
1334 | |||
|
1335 | /* "rgs" stands for "reverse growable set". | |||
|
1336 | It is a representation of a set of integers that can't exceed, but | |||
|
1337 | tend to be close to `max`. | |||
|
1338 | ||||
|
1339 | `body` is a growable bit array covering the range `max-len+1..max`, | |||
|
1340 | in reverse order. | |||
|
1341 | `sum` keeps track of the cardinality of the set. | |||
|
1342 | */ | |||
|
1343 | typedef struct rgs { | |||
|
1344 | int max; | |||
|
1345 | int len; | |||
|
1346 | char *body; | |||
|
1347 | int sum; | |||
|
1348 | } rgs; | |||
|
1349 | ||||
|
1350 | static int rgs_offset(rgs *rgs, int i) | |||
|
1351 | { | |||
|
1352 | return rgs->max - i; | |||
|
1353 | } | |||
|
1354 | ||||
|
1355 | /* returns 1 on success, 0 on failure */ | |||
|
1356 | static int rgs_alloc(rgs *rgs, int max) | |||
|
1357 | { | |||
|
1358 | int new_len = 64; | |||
|
1359 | char *new_body = calloc(new_len, 1); | |||
|
1360 | if (new_body == NULL) | |||
|
1361 | return 0; | |||
|
1362 | rgs->len = new_len; | |||
|
1363 | rgs->body = new_body; | |||
|
1364 | rgs->max = max; | |||
|
1365 | rgs->sum = 0; | |||
|
1366 | return 1; | |||
|
1367 | } | |||
|
1368 | ||||
|
1369 | static bool rgs_get(rgs *rgs, int i) | |||
|
1370 | { | |||
|
1371 | int offset = rgs_offset(rgs, i); | |||
|
1372 | if (offset >= rgs->len) { | |||
|
1373 | return 0; | |||
|
1374 | } | |||
|
1375 | if (offset < 0) { | |||
|
1376 | abort(); | |||
|
1377 | } | |||
|
1378 | return rgs->body[offset]; | |||
|
1379 | } | |||
|
1380 | ||||
|
1381 | /* Realloc `body` to length `new_len`. | |||
|
1382 | Returns -1 when out of memory. */ | |||
|
1383 | static int rgs_realloc(rgs *rgs, int new_len) | |||
|
1384 | { | |||
|
1385 | int old_len = rgs->len; | |||
|
1386 | char *old_body = rgs->body; | |||
|
1387 | char *new_body = calloc(new_len, 1); | |||
|
1388 | assert(new_len >= old_len); | |||
|
1389 | if (new_body == NULL) | |||
|
1390 | return -1; | |||
|
1391 | memcpy(new_body, old_body, old_len); | |||
|
1392 | free(old_body); | |||
|
1393 | rgs->body = new_body; | |||
|
1394 | rgs->len = new_len; | |||
|
1395 | return 0; | |||
|
1396 | } | |||
|
1397 | ||||
|
1398 | /* Realloc the rgs `body` to include the `offset` */ | |||
|
1399 | static int rgs_realloc_amortized(rgs *rgs, int offset) | |||
|
1400 | { | |||
|
1401 | int old_len = rgs->len; | |||
|
1402 | int new_len = old_len * 4; | |||
|
1403 | if (offset >= new_len) | |||
|
1404 | new_len = offset + 1; | |||
|
1405 | return rgs_realloc(rgs, new_len); | |||
|
1406 | } | |||
|
1407 | ||||
|
1408 | /* returns 0 on success, -1 on error */ | |||
|
1409 | static int rgs_set(rgs *rgs, int i, bool v) | |||
|
1410 | { | |||
|
1411 | int offset = rgs_offset(rgs, i); | |||
|
1412 | if (offset >= rgs->len) { | |||
|
1413 | if (v == 0) { | |||
|
1414 | /* no-op change: no need to resize */ | |||
|
1415 | return 0; | |||
|
1416 | } | |||
|
1417 | if (rgs_realloc_amortized(rgs, offset) == -1) | |||
|
1418 | return -1; | |||
|
1419 | } | |||
|
1420 | if (offset < 0) { | |||
|
1421 | abort(); | |||
|
1422 | } | |||
|
1423 | rgs->sum -= rgs->body[offset]; | |||
|
1424 | rgs->sum += v; | |||
|
1425 | rgs->body[offset] = v; | |||
|
1426 | return 0; | |||
|
1427 | } | |||
|
1428 | ||||
|
1429 | /* Add a size_t value to a Python list. Return -1 on failure. */ | |||
|
1430 | static inline int pylist_append_size_t(PyObject *list, size_t v) | |||
|
1431 | { | |||
|
1432 | return pylist_append_owned(list, PyLong_FromSsize_t(v)); | |||
|
1433 | } | |||
|
1434 | ||||
|
1435 | static PyObject *index_headrevsdiff(indexObject *self, PyObject *args) | |||
|
1436 | { | |||
|
1437 | int begin, end; | |||
|
1438 | Py_ssize_t i, j; | |||
|
1439 | PyObject *heads_added = NULL; | |||
|
1440 | PyObject *heads_removed = NULL; | |||
|
1441 | PyObject *res = NULL; | |||
|
1442 | rgs rgs; | |||
|
1443 | rgs.body = NULL; | |||
|
1444 | ||||
|
1445 | if (!PyArg_ParseTuple(args, "ii", &begin, &end)) | |||
|
1446 | goto bail; | |||
|
1447 | ||||
|
1448 | if (!rgs_alloc(&rgs, end)) | |||
|
1449 | goto bail; | |||
|
1450 | ||||
|
1451 | heads_added = PyList_New(0); | |||
|
1452 | if (heads_added == NULL) | |||
|
1453 | goto bail; | |||
|
1454 | heads_removed = PyList_New(0); | |||
|
1455 | if (heads_removed == NULL) | |||
|
1456 | goto bail; | |||
|
1457 | ||||
|
1458 | for (i = end - 1; i >= begin; i--) { | |||
|
1459 | int parents[2]; | |||
|
1460 | ||||
|
1461 | if (rgs_get(&rgs, i)) { | |||
|
1462 | if (rgs_set(&rgs, i, false) == -1) { | |||
|
1463 | goto bail; | |||
|
1464 | }; | |||
|
1465 | } else { | |||
|
1466 | if (pylist_append_size_t(heads_added, i) == -1) { | |||
|
1467 | goto bail; | |||
|
1468 | } | |||
|
1469 | } | |||
|
1470 | ||||
|
1471 | if (index_get_parents(self, i, parents, i) < 0) | |||
|
1472 | goto bail; | |||
|
1473 | for (j = 0; j < 2; j++) { | |||
|
1474 | if (parents[j] >= 0) | |||
|
1475 | if (rgs_set(&rgs, parents[j], true) == -1) { | |||
|
1476 | goto bail; | |||
|
1477 | } | |||
|
1478 | } | |||
|
1479 | } | |||
|
1480 | ||||
|
1481 | while (rgs.sum) { | |||
|
1482 | int parents[2]; | |||
|
1483 | ||||
|
1484 | if (rgs_get(&rgs, i)) { | |||
|
1485 | if (rgs_set(&rgs, i, false) == -1) { | |||
|
1486 | goto bail; | |||
|
1487 | } | |||
|
1488 | if (pylist_append_size_t(heads_removed, i) == -1) { | |||
|
1489 | goto bail; | |||
|
1490 | } | |||
|
1491 | } | |||
|
1492 | ||||
|
1493 | if (index_get_parents(self, i, parents, i) < 0) | |||
|
1494 | goto bail; | |||
|
1495 | for (j = 0; j < 2; j++) { | |||
|
1496 | if (parents[j] >= 0) | |||
|
1497 | if (rgs_set(&rgs, parents[j], false) == -1) { | |||
|
1498 | /* can't actually fail */ | |||
|
1499 | goto bail; | |||
|
1500 | } | |||
|
1501 | } | |||
|
1502 | i--; | |||
|
1503 | } | |||
|
1504 | ||||
|
1505 | if (begin == 0 && end > 0) { | |||
|
1506 | if (pylist_append_size_t(heads_removed, -1) == -1) { | |||
|
1507 | goto bail; | |||
|
1508 | } | |||
|
1509 | } | |||
|
1510 | ||||
|
1511 | if (!(res = PyTuple_Pack(2, heads_removed, heads_added))) { | |||
|
1512 | goto bail; | |||
|
1513 | } | |||
|
1514 | ||||
|
1515 | Py_XDECREF(heads_removed); | |||
|
1516 | Py_XDECREF(heads_added); | |||
|
1517 | free(rgs.body); | |||
|
1518 | return res; | |||
|
1519 | bail: | |||
|
1520 | Py_XDECREF(heads_added); | |||
|
1521 | Py_XDECREF(heads_removed); | |||
|
1522 | free(rgs.body); | |||
|
1523 | return NULL; | |||
|
1524 | } | |||
|
1525 | ||||
1335 | /** |
|
1526 | /** | |
1336 | * Obtain the base revision index entry. |
|
1527 | * Obtain the base revision index entry. | |
1337 | * |
|
1528 | * | |
@@ -3141,6 +3332,8 b' static PyMappingMethods index_mapping_me' | |||||
3141 | static PyMethodDef index_methods[] = { |
|
3332 | static PyMethodDef index_methods[] = { | |
3142 | {"ancestors", (PyCFunction)index_ancestors, METH_VARARGS, |
|
3333 | {"ancestors", (PyCFunction)index_ancestors, METH_VARARGS, | |
3143 | "return the gca set of the given revs"}, |
|
3334 | "return the gca set of the given revs"}, | |
|
3335 | {"headrevsdiff", (PyCFunction)index_headrevsdiff, METH_VARARGS, | |||
|
3336 | "return the set of heads removed/added by a range of commits"}, | |||
3144 | {"commonancestorsheads", (PyCFunction)index_commonancestorsheads, |
|
3337 | {"commonancestorsheads", (PyCFunction)index_commonancestorsheads, | |
3145 | METH_VARARGS, |
|
3338 | METH_VARARGS, | |
3146 | "return the heads of the common ancestors of the given revs"}, |
|
3339 | "return the heads of the common ancestors of the given revs"}, |
General Comments 0
You need to be logged in to leave comments.
Login now