~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/_readdir_pyx.pyx

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2008-09-24 01:43:25 UTC
  • mfrom: (3696.3.12 integration)
  • Revision ID: pqm@pqm.ubuntu.com-20080924014325-ucivgbdmsbuthnqw
(robertc) Accelerate _walkdirs_utf8 on unix platforms. (Robert
        Collins)

Show diffs side-by-side

added added

removed removed

Lines of Context:
29
29
    int errno
30
30
    char *strerror(int errno)
31
31
 
 
32
cdef extern from 'unistd.h':
 
33
    int chdir(char *path)
 
34
    char *getcwd(char *, int size)
 
35
 
 
36
cdef extern from 'stdlib.h':
 
37
    void *malloc(int)
 
38
    void free(void *)
 
39
 
 
40
 
32
41
cdef extern from 'sys/types.h':
33
42
    ctypedef long ssize_t
34
43
    ctypedef unsigned long size_t
 
44
    ctypedef long time_t
 
45
    ctypedef unsigned long ino_t
 
46
    ctypedef unsigned long long off_t
 
47
 
 
48
 
 
49
cdef extern from 'sys/stat.h':
 
50
    cdef struct stat:
 
51
        int st_mode
 
52
        off_t st_size
 
53
        int st_dev
 
54
        ino_t st_ino
 
55
        int st_mtime
 
56
        int st_ctime
 
57
    int lstat(char *path, stat *buf)
 
58
    int S_ISDIR(int mode)
 
59
    int S_ISCHR(int mode)
 
60
    int S_ISBLK(int mode)
 
61
    int S_ISREG(int mode)
 
62
    int S_ISFIFO(int mode)
 
63
    int S_ISLNK(int mode)
 
64
    int S_ISSOCK(int mode)
 
65
 
 
66
 
 
67
cdef extern from 'Python.h':
 
68
    char * PyString_AS_STRING(object)
 
69
    ctypedef int Py_ssize_t # Required for older pyrex versions
 
70
    ctypedef struct PyObject:
 
71
        pass
 
72
    Py_ssize_t PyString_Size(object s)
 
73
    object PyList_GetItem(object lst, Py_ssize_t index)
 
74
    void *PyList_GetItem_object_void "PyList_GET_ITEM" (object lst, int index)
 
75
    int PyList_Append(object lst, object item) except -1
 
76
    void *PyTuple_GetItem_void_void "PyTuple_GET_ITEM" (void* tpl, int index)
 
77
    int PyTuple_SetItem(void *, Py_ssize_t pos, object item) except -1
 
78
    int PyTuple_SetItem_obj "PyTuple_SetItem" (void *, Py_ssize_t pos, PyObject * item) except -1
 
79
    void Py_INCREF(object o)
 
80
    void Py_DECREF(object o)
 
81
    void PyString_Concat(PyObject **string, object newpart)
 
82
 
35
83
 
36
84
cdef extern from 'dirent.h':
37
 
    int DT_UNKNOWN
38
 
    int DT_REG
39
 
    int DT_DIR
40
 
    int DT_FIFO
41
 
    int DT_SOCK
42
 
    int DT_CHR
43
 
    int DT_BLK
44
85
    ctypedef struct dirent:
45
86
        char d_name[256]
46
 
        # this will fail to compile if d_type is not defined.
47
 
        # if this module fails to compile, use the .py version.
48
 
        unsigned char d_type
49
 
        int d_ino
 
87
        ino_t d_ino
50
88
    ctypedef struct DIR
51
89
    # should be DIR *, pyrex barfs.
52
90
    DIR * opendir(char * name)
61
99
_symlink = 'symlink'
62
100
_socket = 'socket'
63
101
_unknown = 'unknown'
64
 
 
65
 
dot = ord('.')
 
102
_missing = 'missing'
66
103
 
67
104
# add a typedef struct dirent dirent to workaround pyrex
68
105
cdef extern from 'readdir.h':
69
106
    pass
70
107
 
71
 
def read_dir(path):
 
108
 
 
109
cdef class _Stat:
 
110
    """Represent a 'stat' result."""
 
111
 
 
112
    cdef stat _st
 
113
 
 
114
    property st_dev:
 
115
        def __get__(self):
 
116
            return self._st.st_dev
 
117
 
 
118
    property st_ino:
 
119
        def __get__(self):
 
120
            return self._st.st_ino
 
121
 
 
122
    property st_mode:
 
123
        def __get__(self):
 
124
            return self._st.st_mode
 
125
 
 
126
    property st_ctime:
 
127
        def __get__(self):
 
128
            return self._st.st_ctime
 
129
 
 
130
    property st_mtime:
 
131
        def __get__(self):
 
132
            return self._st.st_mtime
 
133
 
 
134
    property st_size:
 
135
        def __get__(self):
 
136
            return self._st.st_size
 
137
 
 
138
    def __repr__(self):
 
139
        """Repr is the same as a Stat object.
 
140
 
 
141
        (mode, ino, dev, nlink, uid, gid, size, None(atime), mtime, ctime)
 
142
        """
 
143
        return repr((self.st_mode, 0, 0, 0, 0, 0, self.st_size, None,
 
144
                     self._mtime, self._ctime))
 
145
 
 
146
 
 
147
from bzrlib import osutils
 
148
 
 
149
 
 
150
cdef class UTF8DirReader:
 
151
    """A dir reader for utf8 file systems."""
 
152
 
 
153
    cdef readonly object _safe_utf8
 
154
    cdef _directory, _chardev, _block, _file, _fifo, _symlink
 
155
    cdef _socket, _unknown
 
156
 
 
157
    def __init__(self):
 
158
        self._safe_utf8 = osutils.safe_utf8
 
159
        self._directory = _directory
 
160
        self._chardev = _chardev
 
161
        self._block = _block
 
162
        self._file = _file
 
163
        self._fifo = _fifo
 
164
        self._symlink = _symlink
 
165
        self._socket = _socket
 
166
        self._unknown = _unknown
 
167
 
 
168
    def kind_from_mode(self, int mode):
 
169
        """Get the kind of a path from a mode status."""
 
170
        return self._kind_from_mode(mode)
 
171
 
 
172
    cdef _kind_from_mode(self, int mode):
 
173
        # Files and directories are the most common - check them first.
 
174
        if S_ISREG(mode):
 
175
            return self._file
 
176
        if S_ISDIR(mode):
 
177
            return self._directory
 
178
        if S_ISCHR(mode):
 
179
            return self._chardev
 
180
        if S_ISBLK(mode):
 
181
            return self._block
 
182
        if S_ISLNK(mode):
 
183
            return self._symlink
 
184
        if S_ISFIFO(mode):
 
185
            return self._fifo
 
186
        if S_ISSOCK(mode):
 
187
            return self._socket
 
188
        return self._unknown
 
189
 
 
190
    def top_prefix_to_starting_dir(self, top, prefix=""):
 
191
        """See DirReader.top_prefix_to_starting_dir."""
 
192
        return (self._safe_utf8(prefix), None, None, None,
 
193
            self._safe_utf8(top))
 
194
 
 
195
    def read_dir(self, prefix, top):
 
196
        """Read a single directory from a utf8 file system.
 
197
 
 
198
        All paths in and out are utf8.
 
199
 
 
200
        This sub-function is called when we know the filesystem is already in utf8
 
201
        encoding. So we don't need to transcode filenames.
 
202
 
 
203
        See DirReader.read_dir for details.
 
204
        """
 
205
        #cdef char *_prefix = prefix
 
206
        #cdef char *_top = top
 
207
        # Use C accelerated directory listing.
 
208
        cdef object newval
 
209
        cdef int index
 
210
        cdef int length
 
211
        cdef void * atuple
 
212
        cdef object name
 
213
        cdef PyObject * new_val_obj
 
214
 
 
215
        if PyString_Size(prefix):
 
216
            relprefix = prefix + '/'
 
217
        else:
 
218
            relprefix = ''
 
219
        top_slash = top + '/'
 
220
 
 
221
        # read_dir supplies in should-stat order.
 
222
        # for _, name in sorted(_listdir(top)):
 
223
        result = _read_dir(top)
 
224
        length = len(result)
 
225
        # result.sort()
 
226
        for index from 0 <= index < length:
 
227
            atuple = PyList_GetItem_object_void(result, index)
 
228
            name = <object>PyTuple_GetItem_void_void(atuple, 1)
 
229
            # We have a tuple with (inode, name, None, statvalue, None)
 
230
            # Now edit it:
 
231
            # inode -> path_from_top
 
232
            # direct concat - faster than operator +.
 
233
            new_val_obj = <PyObject *>relprefix
 
234
            Py_INCREF(relprefix)
 
235
            PyString_Concat(&new_val_obj, name)
 
236
            if NULL == new_val_obj:
 
237
                # PyString_Concat will have setup an exception, but how to get
 
238
                # at it?
 
239
                raise Exception("failed to strcat")
 
240
            PyTuple_SetItem_obj(atuple, 0, new_val_obj)
 
241
            # 1st None -> kind
 
242
            newval = self._kind_from_mode(
 
243
                (<_Stat>PyTuple_GetItem_void_void(atuple, 3)).st_mode)
 
244
            Py_INCREF(newval)
 
245
            PyTuple_SetItem(atuple, 2, newval)
 
246
            # 2nd None -> abspath # for all - the caller may need to stat files
 
247
            # etc.
 
248
            # direct concat - faster than operator +.
 
249
            new_val_obj = <PyObject *>top_slash
 
250
            Py_INCREF(top_slash)
 
251
            PyString_Concat(&new_val_obj, name)
 
252
            if NULL == new_val_obj:
 
253
                # PyString_Concat will have setup an exception, but how to get
 
254
                # at it?
 
255
                raise Exception("failed to strcat")
 
256
            PyTuple_SetItem_obj(atuple, 4, new_val_obj)
 
257
        return result
 
258
 
 
259
 
 
260
cdef _read_dir(path):
72
261
    """Like os.listdir, this reads the contents of a directory.
73
262
 
74
263
    :param path: the directory to list.
75
 
    :return: a list of (sort_key, basename) tuples.
 
264
    :return: a list of single-owner (the list) tuples ready for editing into
 
265
        the result tuples walkdirs needs to yield. They contain (inode, name,
 
266
        None, statvalue, None).
76
267
    """
77
268
    cdef DIR *the_dir
78
269
    # currently this needs a fixup - the C code says 'dirent' but should say
80
271
    cdef dirent * entry
81
272
    cdef dirent sentinel
82
273
    cdef char *name
83
 
    the_dir = opendir(path)
 
274
    cdef int stat_result
 
275
    cdef _Stat statvalue
 
276
    cdef char *cwd
 
277
 
 
278
    cwd = getcwd(NULL, 0)
 
279
    if -1 == chdir(path):
 
280
        raise OSError(errno, strerror(errno))
 
281
    the_dir = opendir(".")
84
282
    if NULL == the_dir:
85
283
        raise OSError(errno, strerror(errno))
86
284
    result = []
103
301
                    # done
104
302
                    continue
105
303
            name = entry.d_name
106
 
            if not (name[0] == dot and (
 
304
            if not (name[0] == c"." and (
107
305
                (name[1] == 0) or 
108
 
                (name[1] == dot and name[2] == 0))
 
306
                (name[1] == c"." and name[2] == 0))
109
307
                ):
110
 
                result.append((entry.d_ino, entry.d_name))
 
308
                statvalue = _Stat()
 
309
                stat_result = lstat(entry.d_name, &statvalue._st)
 
310
                if stat_result != 0:
 
311
                    if errno != ENOENT:
 
312
                        raise OSError(errno, strerror(errno))
 
313
                    else:
 
314
                        kind = _missing
 
315
                        statvalue = None
 
316
                # We append a 5-tuple that can be modified in-place by the C
 
317
                # api:
 
318
                # inode to sort on (to replace with top_path)
 
319
                # name (to keep)
 
320
                # kind (None, to set)
 
321
                # statvalue (to keep)
 
322
                # abspath (None, to set)
 
323
                PyList_Append(result, (entry.d_ino, entry.d_name, None,
 
324
                    statvalue, None))
111
325
    finally:
 
326
        if -1 == chdir(cwd):
 
327
            free(cwd)
 
328
            raise OSError(errno, strerror(errno))
 
329
        free(cwd)
112
330
        if -1 == closedir(the_dir):
113
331
            raise OSError(errno, strerror(errno))
114
332
    return result