~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/_walkdirs_win32.pyx

  • Committer: John Arbash Meinel
  • Date: 2008-07-17 03:46:13 UTC
  • mto: This revision was merged to the branch mainline in revision 3557.
  • Revision ID: john@arbash-meinel.com-20080717034613-3cqwmu9mfshqwyet
Some code cleanups.

Remove extra comments.
Use 64 bit integer math when possible.
Use PyList_Append rather than foo.append()
Use PyUnicode_AsUTF8String rather than codecs.encode()
Make sure to raise an exception if the target directory doesn't exist.
Seems to have made a significant performance impact.

Show diffs side-by-side

added added

removed removed

Lines of Context:
21
21
    cdef struct _HANDLE:
22
22
        pass
23
23
    ctypedef _HANDLE *HANDLE
24
 
    ctypedef unsigned int DWORD
 
24
    ctypedef unsigned long DWORD
 
25
    ctypedef unsigned long long __int64
25
26
    ctypedef unsigned short WCHAR
26
27
    cdef struct _FILETIME:
27
28
        DWORD dwHighDateTime
51
52
 
52
53
    cdef DWORD FILE_ATTRIBUTE_READONLY
53
54
    cdef DWORD FILE_ATTRIBUTE_DIRECTORY
 
55
    cdef int ERROR_NO_MORE_FILES
54
56
 
55
57
    cdef int GetLastError()
56
58
 
62
64
    WCHAR *PyUnicode_AS_UNICODE(object)
63
65
    Py_ssize_t PyUnicode_GET_SIZE(object)
64
66
    object PyUnicode_FromUnicode(WCHAR *, Py_ssize_t)
65
 
 
66
 
 
67
 
import codecs
 
67
    int PyList_Append(object, object) except -1
 
68
    object PyUnicode_AsUTF8String(object)
 
69
 
 
70
 
68
71
import operator
69
72
import stat
70
73
 
101
104
    cdef object _top
102
105
    cdef object _prefix
103
106
 
104
 
    cdef object _utf8_encode
105
107
    cdef object _directory_kind
106
108
    cdef object _file_kind
107
109
 
112
114
        self._top = top
113
115
        self._prefix = prefix
114
116
 
115
 
        self._utf8_encode = codecs.getencoder('utf8')
116
117
        self._directory_kind = osutils._directory_kind
117
118
        self._file_kind = osutils._formats[stat.S_IFREG]
118
119
 
119
 
        self._pending = [(osutils.safe_utf8(prefix), None, None, None,
120
 
                          osutils.safe_unicode(top))]
 
120
        self._pending = [(osutils.safe_utf8(prefix), osutils.safe_unicode(top))]
121
121
        self._last_dirblock = None
122
122
 
123
123
    def __iter__(self):
141
141
        return mode_bits
142
142
 
143
143
    cdef object _get_size(self, WIN32_FIND_DATAW *data):
144
 
        return long(data.nFileSizeLow) + (long(data.nFileSizeHigh) << 32)
 
144
        # Pyrex casts a DWORD into a PyLong anyway, so it is safe to do << 32
 
145
        # on a DWORD
 
146
        cdef __int64 val
 
147
        val = ((<__int64>data.nFileSizeHigh) << 32) + data.nFileSizeLow
 
148
        return val
145
149
 
146
150
    cdef object _get_stat_value(self, WIN32_FIND_DATAW *data):
147
151
        """Get the filename and the stat information."""
179
183
        It also uses the epoch 1601-01-01 rather than 1970-01-01
180
184
        (taken from posixmodule.c)
181
185
        """
 
186
        cdef __int64 val
182
187
        # NB: This gives slightly different results versus casting to a 64-bit
183
188
        #     integer and doing integer math before casting into a floating
184
189
        #     point number. But the difference is in the sub millisecond range,
185
190
        #     which doesn't seem critical here.
186
 
        return ((ft.dwHighDateTime * 429.4967296 + ft.dwLowDateTime * 1e-7)
187
 
                - 11644473600.0)
 
191
        # secs between epochs: 11,644,473,600
 
192
        val = ((<__int64>ft.dwHighDateTime) << 32) + ft.dwLowDateTime
 
193
        return (val / 1.0e7) - 11644473600.0
188
194
 
189
195
    def _get_files_in(self, directory, relprefix):
190
196
        cdef WIN32_FIND_DATAW search_data
196
202
        top_star = directory + '*'
197
203
 
198
204
        dirblock = []
199
 
        append = dirblock.append
200
205
 
201
206
        query = PyUnicode_AS_UNICODE(top_star)
202
207
        hFindFile = FindFirstFileW(query, &search_data)
203
208
        if hFindFile == INVALID_HANDLE_VALUE:
204
209
            # Raise an exception? This path doesn't seem to exist
205
 
            last_err = GetLastError()
206
 
            # Could be last_err == ERROR_FILE_NOT_FOUND
207
 
            return []
 
210
            raise WindowsError(GetLastError(), top_star)
208
211
 
209
212
        try:
210
213
            result = 1
214
217
                    result = FindNextFileW(hFindFile, &search_data)
215
218
                    continue
216
219
                name_unicode = self._get_name(&search_data)
217
 
                name_utf8 = self._utf8_encode(name_unicode)[0]
 
220
                name_utf8 = PyUnicode_AsUTF8String(name_unicode)
218
221
                relpath = relprefix + name_utf8
219
222
                abspath = directory + name_unicode
220
 
                append((relpath, name_utf8, 
221
 
                        self._get_kind(&search_data),
222
 
                        self._get_stat_value(&search_data),
223
 
                        abspath))
 
223
                PyList_Append(dirblock, 
 
224
                    (relpath, name_utf8, 
 
225
                     self._get_kind(&search_data),
 
226
                     self._get_stat_value(&search_data),
 
227
                     abspath))
224
228
 
225
229
                result = FindNextFileW(hFindFile, &search_data)
 
230
            # FindNextFileW sets GetLastError() == ERROR_NO_MORE_FILES when it
 
231
            # actually finishes. If we have anything else, then we have a
 
232
            # genuine problem
 
233
            last_err = GetLastError()
 
234
            if last_err != ERROR_NO_MORE_FILES:
 
235
                raise WindowsError(last_err)
226
236
        finally:
227
237
            result = FindClose(hFindFile)
228
238
            if result == 0:
230
240
                pass
231
241
        return dirblock
232
242
 
233
 
        # for record in FindFilesIterator(top_star):
234
 
        #     name = record[-2]
235
 
        #     if name in ('.', '..'):
236
 
        #         continue
237
 
        #     attrib = record[0]
238
 
        #     statvalue = osutils._Win32Stat(record)
239
 
        #     name_utf8 = _utf8_encode(name)[0]
240
 
        #     abspath = top_slash + name
241
 
        #     if DIRECTORY & attrib == DIRECTORY:
242
 
        #         kind = _directory
243
 
        #     else:
244
 
        #         kind = _file
245
 
        #     append((relprefix + name_utf8, name_utf8, kind, statvalue, abspath))
246
 
 
247
 
    def __next__(self):
 
243
    cdef _update_pending(self):
 
244
        """If we had a result before, add the subdirs to pending."""
248
245
        if self._last_dirblock is not None:
249
246
            # push the entries left in the dirblock onto the pending queue
250
247
            # we do this here, because we allow the user to modified the
251
248
            # queue before the next iteration
252
249
            for d in reversed(self._last_dirblock):
253
250
                if d[2] == self._directory_kind:
254
 
                    self._pending.append(d)
255
 
 
 
251
                    self._pending.append((d[0], d[-1]))
 
252
            self._last_dirblock = None
 
253
        
 
254
    def __next__(self):
 
255
        self._update_pending()
256
256
        if not self._pending:
257
257
            raise StopIteration()
258
 
        relroot, _, _, _, top = self._pending.pop()
 
258
        relroot, top = self._pending.pop()
259
259
        # NB: At the moment Pyrex doesn't support Unicode literals, which means
260
260
        # that all of these string literals are going to be upcasted to Unicode
261
261
        # at runtime... :(