~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/_dirstate_helpers_c.pyx

More Cification of iter_changes, smaller functions, explicit type usage.

Show diffs side-by-side

added added

removed removed

Lines of Context:
78
78
# will automatically Py_INCREF and Py_DECREF when appropriate. But for some
79
79
# inner loops, we don't need to do that at all, as the reference only lasts for
80
80
# a very short time.
 
81
# Note that the C API GetItem calls borrow references, so pyrex does the wrong
 
82
# thing if you declare e.g. object PyList_GetItem(object lst, int index) - you
 
83
# need to manually Py_INCREF yourself.
81
84
cdef extern from "Python.h":
82
85
    ctypedef int Py_ssize_t
83
86
    ctypedef struct PyObject:
85
88
    int PyList_Append(object lst, object item) except -1
86
89
    void *PyList_GetItem_object_void "PyList_GET_ITEM" (object lst, int index)
87
90
    void *PyList_GetItem_void_void "PyList_GET_ITEM" (void * lst, int index)
 
91
    object PyList_GET_ITEM(object lst, Py_ssize_t index)
88
92
    int PyList_CheckExact(object)
 
93
    Py_ssize_t PyList_GET_SIZE (object p)
89
94
 
90
95
    void *PyTuple_GetItem_void_void "PyTuple_GET_ITEM" (void* tpl, int index)
91
96
    object PyTuple_GetItem_void_object "PyTuple_GET_ITEM" (void* tpl, int index)
 
97
    object PyTuple_GET_ITEM(object tpl, Py_ssize_t index)
 
98
 
92
99
 
93
100
    char *PyString_AsString(object p)
94
101
    char *PyString_AsString_obj "PyString_AsString" (PyObject *string)
95
102
    char *PyString_AS_STRING_void "PyString_AS_STRING" (void *p)
 
103
    int PyString_AsStringAndSize(object str, char **buffer, Py_ssize_t *length) except -1
96
104
    object PyString_FromString(char *)
97
105
    object PyString_FromStringAndSize(char *, Py_ssize_t)
98
106
    int PyString_Size(object p)
956
964
    cdef object root_entries
957
965
    cdef int root_entries_pos, root_entries_len
958
966
    cdef object root_abspath
959
 
    cdef int path_handled
960
967
    cdef int source_index, target_index
961
968
    cdef int want_unversioned
962
969
    cdef object tree
964
971
    cdef int block_index
965
972
    cdef object current_block
966
973
    cdef int current_block_pos
 
974
    cdef object current_block_list
967
975
    cdef object current_dir_info
 
976
    cdef object current_dir_list
 
977
    cdef int path_index
968
978
    cdef object root_dir_info
969
 
    cdef int path_index
970
979
    cdef object bisect_left
971
980
 
972
981
    def __init__(self, include_unchanged, use_filesystem_for_exec,
1007
1016
        self.dir_iterator = None
1008
1017
        self.block_index = -1
1009
1018
        self.current_block = None
 
1019
        self.current_block_list = None
1010
1020
        self.current_block_pos = -1
1011
1021
        self.current_dir_info = None
 
1022
        self.current_dir_list = None
1012
1023
        self.path_index = 0
1013
1024
        self.root_dir_info = None
1014
1025
        self.bisect_left = bisect.bisect_left
1291
1302
        if (self.block_index < len(self.state._dirblocks) and
1292
1303
            osutils.is_inside(self.current_root, self.state._dirblocks[self.block_index][0])):
1293
1304
            self.current_block = self.state._dirblocks[self.block_index]
 
1305
            self.current_block_list = self.current_block[1]
1294
1306
            self.current_block_pos = 0
1295
1307
        else:
1296
1308
            self.current_block = None
 
1309
            self.current_block_list = None
1297
1310
 
1298
1311
    def __next__(self):
1299
1312
        # Simple thunk to allow tail recursion without pyrex confusion
1350
1363
        cdef object current_dirname, current_blockname
1351
1364
        cdef char * current_dirname_c, * current_blockname_c
1352
1365
        cdef int advance_entry, advance_path
 
1366
        cdef int path_handled
1353
1367
        uninteresting = self.uninteresting
1354
1368
        searched_specific_files = self.searched_specific_files
1355
1369
        # Are we walking a root?
1397
1411
                # (tail recursion, can do a loop once the full structure is
1398
1412
                # known).
1399
1413
                return self._iter_next()
1400
 
            self.path_handled = 0
 
1414
            path_handled = 0
1401
1415
            self.root_entries_pos = 0
1402
1416
            # XXX Clarity: This loop is duplicated a out the self.current_root
1403
1417
            # is None guard above: if we return from it, it completes there
1404
1418
            # (and the following if block cannot trigger because
1405
 
            # self.path_handled must be true, so the if block is not
1406
 
            # duplicated.
 
1419
            # path_handled must be true, so the if block is not # duplicated.
1407
1420
            while self.root_entries_pos < self.root_entries_len:
1408
1421
                entry = self.root_entries[self.root_entries_pos]
1409
1422
                self.root_entries_pos = self.root_entries_pos + 1
1410
1423
                result = self._process_entry(entry, self.root_dir_info)
1411
1424
                if result is not None:
1412
 
                    self.path_handled = -1
 
1425
                    path_handled = -1
1413
1426
                    if result is not self.uninteresting:
1414
1427
                        return result
1415
1428
            # handle unversioned specified paths:
1416
 
            if self.want_unversioned and not self.path_handled and self.root_dir_info:
 
1429
            if self.want_unversioned and not path_handled and self.root_dir_info:
1417
1430
                new_executable = bool(
1418
1431
                    stat.S_ISREG(self.root_dir_info[3].st_mode)
1419
1432
                    and stat.S_IEXEC & self.root_dir_info[3].st_mode)
1430
1443
            # per-root setup logic.
1431
1444
        if self.current_dir_info is None and self.current_block is None:
1432
1445
            # setup iteration of this root:
 
1446
            self.current_dir_list = None
1433
1447
            if self.root_dir_info and self.root_dir_info[2] == 'tree-reference':
1434
1448
                self.current_dir_info = None
1435
1449
            else:
1438
1452
                self.path_index = 0
1439
1453
                try:
1440
1454
                    self.current_dir_info = self.dir_iterator.next()
 
1455
                    self.current_dir_list = self.current_dir_info[1]
1441
1456
                except OSError, e:
1442
1457
                    # there may be directories in the inventory even though
1443
1458
                    # this path is not a file on disk: so mark it as end of
1463
1478
                else:
1464
1479
                    if self.current_dir_info[0][0] == '':
1465
1480
                        # remove .bzr from iteration
1466
 
                        bzr_index = self.bisect_left(self.current_dir_info[1], ('.bzr',))
1467
 
                        if self.current_dir_info[1][bzr_index][0] != '.bzr':
 
1481
                        bzr_index = self.bisect_left(self.current_dir_list, ('.bzr',))
 
1482
                        if self.current_dir_list[bzr_index][0] != '.bzr':
1468
1483
                            raise AssertionError()
1469
 
                        del self.current_dir_info[1][bzr_index]
 
1484
                        del self.current_dir_list[bzr_index]
1470
1485
            initial_key = (self.current_root, '', '')
1471
1486
            self.block_index, _ = self.state._find_block_index_from_key(initial_key)
1472
1487
            if self.block_index == 0:
1478
1493
        # are exhausted. 
1479
1494
        while (self.current_dir_info is not None
1480
1495
            or self.current_block is not None):
 
1496
            # Uncommon case - a missing directory or an unversioned directory:
1481
1497
            if (self.current_dir_info and self.current_block
1482
1498
                and self.current_dir_info[0][0] != self.current_block[0]):
1483
1499
                # Work around pyrex broken heuristic - current_dirname has
1506
1522
                    # if (B) then we should ignore it, because we don't
1507
1523
                    # recurse into unknown directories.
1508
1524
                    # We are doing a loop
1509
 
                    while self.path_index < len(self.current_dir_info[1]):
1510
 
                        current_path_info = self.current_dir_info[1][self.path_index]
 
1525
                    while self.path_index < len(self.current_dir_list):
 
1526
                        current_path_info = self.current_dir_list[self.path_index]
1511
1527
                        # dont descend into this unversioned path if it is
1512
1528
                        # a dir
1513
1529
                        if current_path_info[2] in ('directory',
1514
1530
                                                    'tree-reference'):
1515
 
                            del self.current_dir_info[1][self.path_index]
 
1531
                            del self.current_dir_list[self.path_index]
1516
1532
                            self.path_index = self.path_index - 1
1517
1533
                        self.path_index = self.path_index + 1
1518
1534
                        if self.want_unversioned:
1534
1550
                                (None, new_executable))
1535
1551
                    # This dir info has been handled, go to the next
1536
1552
                    self.path_index = 0
 
1553
                    self.current_dir_list = None
1537
1554
                    try:
1538
1555
                        self.current_dir_info = self.dir_iterator.next()
 
1556
                        self.current_dir_list = self.current_dir_info[1]
1539
1557
                    except StopIteration:
1540
1558
                        self.current_dir_info = None
1541
1559
                else: #(dircmp > 0)
1546
1564
                    # because that should have already been handled, but we
1547
1565
                    # need to handle all of the files that are contained
1548
1566
                    # within.
1549
 
                    while self.current_block_pos < len(self.current_block[1]):
1550
 
                        current_entry = self.current_block[1][self.current_block_pos]
 
1567
                    while self.current_block_pos < len(self.current_block_list):
 
1568
                        current_entry = self.current_block_list[self.current_block_pos]
1551
1569
                        self.current_block_pos = self.current_block_pos + 1
1552
1570
                        # entry referring to file not present on disk.
1553
1571
                        # advance the entry only, after processing.
1558
1576
                    self.block_index = self.block_index + 1
1559
1577
                    self._update_current_block()
1560
1578
                continue # next loop-on-block/dir
 
1579
            result = self._loop_one_block()
 
1580
            if result is not None:
 
1581
                return result
 
1582
        if len(self.search_specific_files):
 
1583
            # More supplied paths to process
 
1584
            self.current_root = None
 
1585
            return self._iter_next()
 
1586
        raise StopIteration()
 
1587
 
 
1588
    cdef object _maybe_tree_ref(self, current_path_info):
 
1589
        if self.tree._directory_is_tree_reference(
 
1590
            self.utf8_decode(current_path_info[0])[0]):
 
1591
            return current_path_info[:2] + \
 
1592
                ('tree-reference',) + current_path_info[3:]
 
1593
        else:
 
1594
            return current_path_info
 
1595
 
 
1596
    cdef object _loop_one_block(self):
1561
1597
            # current_dir_info and current_block refer to the same directory -
1562
1598
            # this is the common case code.
1563
1599
            # Assign local variables for current path and entry:
1564
 
            if (self.current_block and
1565
 
                self.current_block_pos < len(self.current_block[1])):
1566
 
                current_entry = self.current_block[1][self.current_block_pos]
 
1600
            cdef object current_entry
 
1601
            cdef object current_path_info
 
1602
            cdef int path_handled
 
1603
            # cdef char * temp_str
 
1604
            # cdef Py_ssize_t temp_str_length
 
1605
            # PyString_AsStringAndSize(disk_kind, &temp_str, &temp_str_length)
 
1606
            # if not strncmp(temp_str, "directory", temp_str_length):
 
1607
            if (self.current_block is not None and
 
1608
                self.current_block_pos < PyList_GET_SIZE(self.current_block_list)):
 
1609
                current_entry = PyList_GET_ITEM(self.current_block_list,
 
1610
                    self.current_block_pos)
 
1611
                # accomodate pyrex
 
1612
                Py_INCREF(current_entry)
1567
1613
            else:
1568
1614
                current_entry = None
1569
 
            if (self.current_dir_info and
1570
 
                self.path_index < len(self.current_dir_info[1])):
1571
 
                current_path_info = self.current_dir_info[1][self.path_index]
1572
 
                if current_path_info[2] == 'directory':
1573
 
                    if self.tree._directory_is_tree_reference(
1574
 
                        self.utf8_decode(current_path_info[0])[0]):
1575
 
                        current_path_info = current_path_info[:2] + \
1576
 
                            ('tree-reference',) + current_path_info[3:]
 
1615
            if (self.current_dir_info is not None and
 
1616
                self.path_index < PyList_GET_SIZE(self.current_dir_list)):
 
1617
                current_path_info = PyList_GET_ITEM(self.current_dir_list,
 
1618
                    self.path_index)
 
1619
                # accomodate pyrex
 
1620
                Py_INCREF(current_path_info)
 
1621
                disk_kind = PyTuple_GET_ITEM(current_path_info, 2)
 
1622
                # accomodate pyrex
 
1623
                Py_INCREF(disk_kind)
 
1624
                if disk_kind == "directory":
 
1625
                    current_path_info = self._maybe_tree_ref(current_path_info)
1577
1626
            else:
1578
1627
                current_path_info = None
1579
 
            self.path_handled = 0
1580
1628
            while (current_entry is not None or current_path_info is not None):
1581
1629
                advance_entry = -1
1582
1630
                advance_path = -1
1583
1631
                result = None
 
1632
                path_handled = 0
1584
1633
                if current_entry is None:
1585
1634
                    # unversioned -  the check for path_handled when the path
1586
1635
                    # is advanced will yield this path if needed.
1616
1665
                else:
1617
1666
                    result = self._process_entry(current_entry, current_path_info)
1618
1667
                    if result is not None:
1619
 
                        self.path_handled = -1
 
1668
                        path_handled = -1
1620
1669
                        if result is self.uninteresting:
1621
1670
                            result = None
1622
1671
                # >- loop control starts here:
1623
1672
                # >- entry
1624
1673
                if advance_entry and current_entry is not None:
1625
1674
                    self.current_block_pos = self.current_block_pos + 1
1626
 
                    if self.current_block_pos < len(self.current_block[1]):
1627
 
                        current_entry = self.current_block[1][self.current_block_pos]
 
1675
                    if self.current_block_pos < PyList_GET_SIZE(self.current_block_list):
 
1676
                        current_entry = self.current_block_list[self.current_block_pos]
1628
1677
                    else:
1629
1678
                        current_entry = None
1630
1679
                # >- path
1631
1680
                if advance_path and current_path_info is not None:
1632
 
                    if not self.path_handled:
 
1681
                    if not path_handled:
1633
1682
                        # unversioned in all regards
1634
1683
                        if self.want_unversioned:
1635
1684
                            new_executable = bool(
1654
1703
                        # dont descend into this unversioned path if it is
1655
1704
                        # a dir
1656
1705
                        if current_path_info[2] in ('directory'):
1657
 
                            del self.current_dir_info[1][self.path_index]
 
1706
                            del self.current_dir_list[self.path_index]
1658
1707
                            self.path_index = self.path_index - 1
1659
1708
                    # dont descend the disk iterator into any tree 
1660
1709
                    # paths.
1661
1710
                    if current_path_info[2] == 'tree-reference':
1662
 
                        del self.current_dir_info[1][self.path_index]
 
1711
                        del self.current_dir_list[self.path_index]
1663
1712
                        self.path_index = self.path_index - 1
1664
1713
                    self.path_index = self.path_index + 1
1665
 
                    if self.path_index < len(self.current_dir_info[1]):
1666
 
                        current_path_info = self.current_dir_info[1][self.path_index]
 
1714
                    if self.path_index < len(self.current_dir_list):
 
1715
                        current_path_info = self.current_dir_list[self.path_index]
1667
1716
                        if current_path_info[2] == 'directory':
1668
 
                            if self.tree._directory_is_tree_reference(
1669
 
                                current_path_info[0].decode('utf8')):
1670
 
                                current_path_info = current_path_info[:2] + \
1671
 
                                    ('tree-reference',) + current_path_info[3:]
 
1717
                            current_path_info = self._maybe_tree_ref(
 
1718
                                current_path_info)
1672
1719
                    else:
1673
1720
                        current_path_info = None
1674
 
                    self.path_handled = 0
1675
1721
                if result is not None:
1676
1722
                    # Found a result on this pass, yield it
1677
1723
                    return result
1680
1726
                self._update_current_block()
1681
1727
            if self.current_dir_info is not None:
1682
1728
                self.path_index = 0
 
1729
                self.current_dir_list = None
1683
1730
                try:
1684
1731
                    self.current_dir_info = self.dir_iterator.next()
 
1732
                    self.current_dir_list = self.current_dir_info[1]
1685
1733
                except StopIteration:
1686
1734
                    self.current_dir_info = None
1687
 
        if len(self.search_specific_files):
1688
 
            # More supplied paths to process
1689
 
            self.current_root = None
1690
 
            return self._iter_next()
1691
 
        raise StopIteration()