~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/osutils.py

Streamline _walkdirs_utf8 for utf8 file systems, reducing time to traverse a mozilla tree from 1s to .6 seconds. (Robert Collins)

Show diffs side-by-side

added added

removed removed

Lines of Context:
123
123
 
124
124
_directory_kind = 'directory'
125
125
 
126
 
_formats = {
127
 
    stat.S_IFDIR:_directory_kind,
128
 
    stat.S_IFCHR:'chardev',
129
 
    stat.S_IFBLK:'block',
130
 
    stat.S_IFREG:'file',
131
 
    stat.S_IFIFO:'fifo',
132
 
    stat.S_IFLNK:'symlink',
133
 
    stat.S_IFSOCK:'socket',
134
 
}
135
 
 
136
 
 
137
 
def file_kind_from_stat_mode(stat_mode, _formats=_formats, _unknown='unknown'):
138
 
    """Generate a file kind from a stat mode. This is used in walkdirs.
139
 
 
140
 
    Its performance is critical: Do not mutate without careful benchmarking.
141
 
    """
142
 
    try:
143
 
        return _formats[stat_mode & 0170000]
144
 
    except KeyError:
145
 
        return _unknown
146
 
 
147
 
 
148
 
def file_kind(f, _lstat=os.lstat, _mapper=file_kind_from_stat_mode):
149
 
    try:
150
 
        return _mapper(_lstat(f).st_mode)
151
 
    except OSError, e:
152
 
        if getattr(e, 'errno', None) in (errno.ENOENT, errno.ENOTDIR):
153
 
            raise errors.NoSuchFile(f)
154
 
        raise
155
 
 
156
 
 
157
126
def get_umask():
158
127
    """Return the current umask"""
159
128
    # Assume that people aren't messing with the umask while running
1201
1170
    _lstat = os.lstat
1202
1171
    _directory = _directory_kind
1203
1172
    _listdir = os.listdir
1204
 
    _kind_from_mode = _formats.get
 
1173
    _kind_from_mode = file_kind_from_stat_mode
1205
1174
    pending = [(safe_unicode(prefix), "", _directory, None, safe_unicode(top))]
1206
1175
    while pending:
1207
1176
        # 0 - relpath, 1- basename, 2- kind, 3- stat, 4-toppath
1223
1192
            for name in names:
1224
1193
                abspath = top_slash + name
1225
1194
                statvalue = _lstat(abspath)
1226
 
                kind = _kind_from_mode(statvalue.st_mode & 0170000, 'unknown')
 
1195
                kind = _kind_from_mode(statvalue.st_mode)
1227
1196
                append((relprefix + name, name, kind, statvalue, abspath))
1228
1197
        yield (relroot, top), dirblock
1229
1198
 
1292
1261
            # ANSI_X3.4-1968 is a form of ASCII
1293
1262
            _selected_dir_reader = UnicodeDirReader()
1294
1263
        else:
1295
 
            _selected_dir_reader = UTF8DirReader()
 
1264
            try:
 
1265
                from bzrlib._readdir_pyx import UTF8DirReader
 
1266
            except ImportError:
 
1267
                # No optimised code path
 
1268
                _selected_dir_reader = UnicodeDirReader()
 
1269
            else:
 
1270
                _selected_dir_reader = UTF8DirReader()
1296
1271
    # 0 - relpath, 1- basename, 2- kind, 3- stat, 4-toppath
1297
1272
    # But we don't actually uses 1-3 in pending, so set them to None
1298
 
    pending = [_selected_dir_reader.top_prefix_to_starting_dir(top, prefix)]
 
1273
    pending = [[_selected_dir_reader.top_prefix_to_starting_dir(top, prefix)]]
1299
1274
    read_dir = _selected_dir_reader.read_dir
1300
1275
    _directory = _directory_kind
1301
1276
    while pending:
1302
 
        relroot, _, _, _, top = pending.pop()
1303
 
        dirblock = read_dir(relroot, top)
 
1277
        relroot, _, _, _, top = pending[-1].pop()
 
1278
        if not pending[-1]:
 
1279
            pending.pop()
 
1280
        dirblock = sorted(read_dir(relroot, top))
1304
1281
        yield (relroot, top), dirblock
1305
1282
        # push the user specified dirs from dirblock
1306
 
        pending.extend(d for d in reversed(dirblock) if d[2] == _directory)
1307
 
 
1308
 
 
1309
 
class UTF8DirReader(DirReader):
1310
 
    """A dir reader for utf8 file systems."""
1311
 
 
1312
 
    def top_prefix_to_starting_dir(self, top, prefix=""):
1313
 
        """See DirReader.top_prefix_to_starting_dir."""
1314
 
        return (safe_utf8(prefix), None, None, None, safe_utf8(top))
1315
 
 
1316
 
    def read_dir(self, prefix, top):
1317
 
        """Read a single directory from a utf8 file system.
1318
 
 
1319
 
        All paths in and out are utf8.
1320
 
 
1321
 
        This sub-function is called when we know the filesystem is already in utf8
1322
 
        encoding. So we don't need to transcode filenames.
1323
 
 
1324
 
        See DirReader.read_dir for details.
1325
 
        """
1326
 
        _lstat = os.lstat
1327
 
        # Use C accelerated directory listing.
1328
 
        _listdir = _read_dir
1329
 
        _kind_from_mode = _formats.get
1330
 
 
1331
 
        if prefix:
1332
 
            relprefix = prefix + '/'
1333
 
        else:
1334
 
            relprefix = ''
1335
 
        top_slash = top + '/'
1336
 
 
1337
 
        dirblock = []
1338
 
        append = dirblock.append
1339
 
        # read_dir supplies in should-stat order.
1340
 
        for _, name in sorted(_listdir(top)):
1341
 
            abspath = top_slash + name
1342
 
            statvalue = _lstat(abspath)
1343
 
            kind = _kind_from_mode(statvalue.st_mode & 0170000, 'unknown')
1344
 
            append((relprefix + name, name, kind, statvalue, abspath))
1345
 
        dirblock.sort()
1346
 
        return dirblock
 
1283
        next = [d for d in reversed(dirblock) if d[2] == _directory]
 
1284
        if next:
 
1285
            pending.append(next)
1347
1286
 
1348
1287
 
1349
1288
class UnicodeDirReader(DirReader):
1374
1313
        _utf8_encode = self._utf8_encode
1375
1314
        _lstat = os.lstat
1376
1315
        _listdir = os.listdir
1377
 
        _kind_from_mode = _formats.get
 
1316
        _kind_from_mode = file_kind_from_stat_mode
1378
1317
 
1379
1318
        if prefix:
1380
1319
            relprefix = prefix + '/'
1388
1327
            name_utf8 = _utf8_encode(name)[0]
1389
1328
            abspath = top_slash + name
1390
1329
            statvalue = _lstat(abspath)
1391
 
            kind = _kind_from_mode(statvalue.st_mode & 0170000, 'unknown')
 
1330
            kind = _kind_from_mode(statvalue.st_mode)
1392
1331
            append((relprefix + name_utf8, name_utf8, kind, statvalue, abspath))
1393
1332
        return dirblock
1394
1333
 
1608
1547
    return open(filename, 'rU').read()
1609
1548
 
1610
1549
 
1611
 
try:
1612
 
    from bzrlib._readdir_pyx import read_dir as _read_dir
1613
 
except ImportError:
1614
 
    from bzrlib._readdir_py import read_dir as _read_dir
 
1550
def file_kind_from_stat_mode_thunk(mode):
 
1551
    global file_kind_from_stat_mode
 
1552
    if file_kind_from_stat_mode is file_kind_from_stat_mode_thunk:
 
1553
        try:
 
1554
            from bzrlib._readdir_pyx import UTF8DirReader
 
1555
            file_kind_from_stat_mode = UTF8DirReader().kind_from_mode
 
1556
        except ImportError:
 
1557
            from bzrlib._readdir_py import (
 
1558
                _kind_from_mode as _file_kind_from_stat_mode
 
1559
                )
 
1560
    return file_kind_from_stat_mode(mode)
 
1561
file_kind_from_stat_mode = file_kind_from_stat_mode_thunk
 
1562
 
 
1563
 
 
1564
def file_kind(f, _lstat=os.lstat):
 
1565
    try:
 
1566
        return file_kind_from_stat_mode(_lstat(f).st_mode)
 
1567
    except OSError, e:
 
1568
        if getattr(e, 'errno', None) in (errno.ENOENT, errno.ENOTDIR):
 
1569
            raise errors.NoSuchFile(f)
 
1570
        raise
 
1571
 
 
1572