[PATCH] fast osutil.c for win32 - incorporated review notes
Petr Kodl
petrkodl at gmail.com
Fri Sep 5 15:55:12 UTC 2008
# HG changeset patch
# User Petr Kodl <petrkodl at gmail.com>
# Date 1220629879 14400
# Node ID 6d7c564ed3a9e325cb3ffcfadf7001fbde274968
# Parent 4e62be0208d3465cf241d8fb6fb7c9ce125a490b
imported patch fast osutil.c for Win32
diff -r 4e62be0208d3 -r 6d7c564ed3a9 mercurial/osutil.c
--- a/mercurial/osutil.c Fri Sep 05 11:04:36 2008 +0200
+++ b/mercurial/osutil.c Fri Sep 05 11:51:19 2008 -0400
@@ -7,32 +7,68 @@
the GNU General Public License, incorporated herein by reference.
*/
-#define _ATFILE_SOURCE
-#include <Python.h>
+#define _ATFILE_SOURCE
+
+#include <Python.h>
+#include <string.h>
+
+#ifdef _WIN32
+#include <windows.h>
+#include <malloc.h> /* for _alloca */
+#else
#include <dirent.h>
#include <fcntl.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
+#endif
+
+#ifdef _WIN32
+/*
+minimal stat struct compatible with hg expectations
+supporting large files (64 bit size)
+*/
+struct non_posix_stat
+{
+ int st_dev;
+ int st_mode;
+ int st_nlink;
+ __int64 st_size;
+ int st_mtime;
+ int st_ctime;
+};
+typedef struct non_posix_stat my_stat;
+#else
+typedef struct stat my_stat;
+#endif
+
struct listdir_stat {
PyObject_HEAD
- struct stat st;
+ my_stat st;
};
#define listdir_slot(name) \
- static PyObject *listdir_stat_##name(PyObject *self, void *x) \
- { \
- return PyInt_FromLong(((struct listdir_stat *)self)->st.name); \
- }
+ static PyObject *listdir_stat_##name(PyObject *self, void *x) \
+ { \
+ return PyInt_FromLong(((struct listdir_stat *)self)->st.name); \
+ }
listdir_slot(st_dev)
listdir_slot(st_mode)
listdir_slot(st_nlink)
-listdir_slot(st_size)
listdir_slot(st_mtime)
listdir_slot(st_ctime)
+#ifdef _WIN32
+static PyObject *listdir_stat_st_size(PyObject *self, void *x)
+{
+ return PyLong_FromLongLong(
+ (PY_LONG_LONG)((struct listdir_stat *)self)->st.st_size);
+}
+#else
+listdir_slot(st_size)
+#endif
static struct PyGetSetDef listdir_stat_getsets[] = {
{"st_dev", listdir_stat_st_dev, 0, 0, 0},
@@ -95,6 +131,202 @@
0, /* tp_alloc */
listdir_stat_new, /* tp_new */
};
+
+#ifdef _WIN32
+
+static int to_python_time(FILETIME* ms_time)
+{
+ /* this is number of 100-nanosecond between epoch and January 1 1601 */
+ static __int64 a0 = (__int64)134774L*(__int64)24L
+ *(__int64)3600L*(__int64)1000L
+ *(__int64)1000L*(__int64)10L;
+ /* conversion factor back to 1s resoluion required by Python ctime */
+ static __int64 a1 = 1000*1000*10;
+ __int64 tmp;
+ memcpy(&tmp,ms_time,sizeof(__int64));
+ return (int)((tmp-a0)/a1);
+}
+
+static int allow_unicode()
+{
+ static int allow = -1;
+ /* unicode supported for NT */
+ if(allow==-1) allow = (GetVersion() < 0x80000000) ? 1 : 0;
+ return allow;
+}
+
+static PyObject *listdir(PyObject *self, PyObject *args, PyObject *kwargs)
+{
+ PyObject *pathobj = NULL,
+ *statobj = NULL,
+ *list = NULL,
+ *items = NULL,
+ *ctor_args = NULL,
+ *item0 = NULL,
+ *item1 = NULL,
+ *py_st = NULL;
+ HANDLE fh = 0;
+ struct non_posix_stat* stp=0;
+ static char *kwlist[] = { "path", "stat", NULL };
+ if(PyArg_ParseTupleAndKeywords(args, kwargs, "O|O:listdir",
+ kwlist, &pathobj, &statobj))
+ {
+ int keepstat= statobj && PyObject_IsTrue(statobj);
+ int unicode = allow_unicode() && PyUnicode_CheckExact(pathobj);
+ WIN32_FIND_DATAA fd_a;
+ WIN32_FIND_DATAW fd_w;
+ if(unicode)
+ {
+ Py_ssize_t len = PyUnicode_GET_SIZE(pathobj);
+ wchar_t *wpath = _alloca((len+5)*sizeof(wchar_t));
+ memset(wpath, 0, (len+5)*sizeof(wchar_t));
+ if(PyUnicode_AsWideChar((PyUnicodeObject*)pathobj, wpath, len)!=len)
+ return 0;
+ if(len>0 && wpath[len-1]!=L':'
+ && wpath[len-1]!=L'/' && wpath[len-1]!='\\')
+ wpath[len++]=L'\\';
+ wcscpy(wpath+len, L"*.*");
+ fh = FindFirstFileW(wpath, &fd_w);
+ }
+ else if(PyString_CheckExact(pathobj))
+ {
+ Py_ssize_t len = PyString_GET_SIZE(pathobj);
+ char path[_MAX_PATH];
+ strncpy(path, PyString_AS_STRING(pathobj), _MAX_PATH);
+ if(len>0 && path[len-1]!=':'
+ && path[len-1]!='/' && path[len-1]!='\\')
+ path[len++]='\\';
+ strcpy(path+len, "*.*");
+ fh = FindFirstFileA(path, &fd_a);
+ }
+ else
+ {
+ PyErr_SetString(PyExc_TypeError,
+ "listdir - expected string or unicode as first argument");
+ goto error;
+ }
+ if(INVALID_HANDLE_VALUE!=fh)
+ {
+ list = PyList_New(0);
+ ctor_args = PyTuple_New(0);
+ if(!list || !ctor_args)
+ {
+ PyErr_NoMemory();
+ goto error;
+ }
+ do
+ {
+ #define FD(NAME) (unicode ? fd_w.NAME : fd_a.NAME)
+ int isdir = (FD(dwFileAttributes) & FILE_ATTRIBUTE_DIRECTORY);
+ int isro = (FD(dwFileAttributes) & FILE_ATTRIBUTE_READONLY);
+ if( !isdir
+ || (unicode && wcscmp(fd_w.cFileName, L".")
+ && wcscmp(fd_w.cFileName, L".."))
+ || (!unicode&& strcmp(fd_a.cFileName, ".")
+ && strcmp(fd_a.cFileName, "..")))
+ {
+ items = PyTuple_New(keepstat ? 3 : 2);
+ item0 = unicode ?
+ PyUnicode_FromWideChar(fd_w.cFileName,
+ wcslen(fd_w.cFileName))
+ : PyString_FromString(fd_a.cFileName);
+ item1 = PyInt_FromLong(isdir ? _S_IFDIR : _S_IFREG);
+ if(!items || !item0 || !item1)
+ {
+ PyErr_NoMemory();
+ goto error;
+ }
+ PyTuple_SetItem(items, 0, item0);
+ PyTuple_SetItem(items, 1, item1);
+ item0 = item1 = 0;
+ if(keepstat)
+ {
+ py_st = PyObject_CallObject(
+ (PyObject *)&listdir_stat_type,
+ ctor_args);
+ if(!py_st)
+ {
+ PyErr_NoMemory();
+ goto error;
+ }
+ stp = &((struct listdir_stat *)py_st)->st;
+ stp->st_mtime = to_python_time(unicode
+ ? &fd_w.ftLastWriteTime
+ : &fd_a.ftLastWriteTime);
+ stp->st_ctime = to_python_time(unicode
+ ? &fd_w.ftCreationTime
+ : &fd_a.ftLastWriteTime);
+ stp->st_dev = 0;
+ stp->st_size = 0;
+ stp->st_mode = (isdir ? (S_IFDIR | 0111) : S_IFREG)
+ | (isro ? 0444 : 0666);
+ if(!isdir)
+ {
+ stp->st_size = (__int64)(FD(nFileSizeHigh)<<32)
+ + FD(nFileSizeLow);
+ if(!unicode)
+ {
+ char* dot = strrchr(fd_a.cFileName, '.');
+ if (dot)
+ {
+ if( !stricmp(dot, ".bat")
+ || !stricmp(dot, ".cmd")
+ || !stricmp(dot, ".exe")
+ || !stricmp(dot, ".com"))
+ stp->st_mode |= 0111;
+ }
+ }
+ else
+ {
+ wchar_t* dot = wcsrchr(fd_w.cFileName, L'.');
+ if (dot)
+ {
+ if( !_wcsicmp(dot, L".bat")
+ || !_wcsicmp(dot, L".cmd")
+ || !_wcsicmp(dot, L".exe")
+ || !_wcsicmp(dot, L".com"))
+ stp->st_mode |= 0111;
+ }
+ }
+ }
+ PyTuple_SET_ITEM(items, 2, py_st);
+ py_st = 0;
+ }
+ if(-1==PyList_Append(list, items))
+ {
+ goto error;
+ }
+ Py_XDECREF(items);
+ items = 0;
+ }
+ #undef FD
+ }
+ while(unicode ? FindNextFileW(fh, &fd_w)
+ : FindNextFileA(fh, &fd_a));
+ Py_XDECREF(ctor_args); ctor_args=0;
+ if(GetLastError()!=ERROR_NO_MORE_FILES || !FindClose(fh))
+ {
+ PyErr_SetExcFromWindowsErr(PyExc_OSError, GetLastError());
+ goto error;
+ }
+ fh = 0;
+ if(-1==PyList_Sort(list)) goto error;
+ return list;
+ }
+ else
+ PyErr_SetExcFromWindowsErr(PyExc_OSError, GetLastError());
+ }
+error:
+ Py_XDECREF(list);
+ Py_XDECREF(ctor_args);
+ Py_XDECREF(items);
+ Py_XDECREF(item0);
+ Py_XDECREF(item1);
+ if(fh) FindClose(fh);
+ return 0;
+}
+
+#else
static PyObject *listfiles(PyObject *list, DIR *dir,
int keep_stat, int *need_stat)
@@ -200,7 +432,7 @@
#endif
if (ret == -1)
return PyErr_SetFromErrnoWithFilename(PyExc_OSError,
- path);
+ path);
if (kind == -1) {
if (S_ISREG(stp->st_mode))
@@ -295,7 +527,7 @@
closedir(dir);
return err ? err : list;
}
-
+#endif
static char osutil_doc[] = "Native operating system services.";
diff -r 4e62be0208d3 -r 6d7c564ed3a9 setup.py
--- a/setup.py Fri Sep 05 11:04:36 2008 +0200
+++ b/setup.py Fri Sep 05 11:51:19 2008 -0400
@@ -101,6 +101,13 @@
packages = ['mercurial', 'mercurial.hgweb', 'hgext', 'hgext.convert',
'hgext.highlight']
+
+try:
+ import msvcrt
+ ext_modules.append(Extension('mercurial.osutil', ['mercurial/osutil.c']))
+except ImportError:
+ pass
+
try:
import posix
ext_modules.append(Extension('mercurial.osutil', ['mercurial/osutil.c']))
More information about the Mercurial-devel
mailing list