[PATCH] fast osutil.c for win32

Petr Kodl petrkodl at gmail.com
Fri Sep 5 08:52:33 CDT 2008


# HG changeset patch
# User Petr Kodl <petrkodl at gmail.com>
# Date 1220622704 14400
# Node ID e7a4e81078f5cafafe089dea11b79e00171e1305
# Parent  4e62be0208d3465cf241d8fb6fb7c9ce125a490b
fast osutil.c for win32

diff -r 4e62be0208d3 -r e7a4e81078f5 mercurial/osutil.c
--- a/mercurial/osutil.c	Fri Sep 05 11:04:36 2008 +0200
+++ b/mercurial/osutil.c	Fri Sep 05 09:51:44 2008 -0400
@@ -7,18 +7,46 @@
  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) \
@@ -30,9 +58,16 @@
 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 +130,174 @@
 	0,                         /* tp_alloc */
 	listdir_stat_new,          /* tp_new */
 };
+
+#ifdef _WIN32 
+
+static int to_python_time(FILETIME* ms_time)
+{
+    static __int64 a0 = (__int64)134774L*(__int64)24L*(__int64)3600L*(__int64)1000L*(__int64)1000L*(__int64)10L;
+    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;
+    if(allow==-1) allow = (GetVersion() < 0x80000000) ? 1 : 0; /* unicode supported for NT */
+	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)
@@ -295,7 +498,7 @@
 		closedir(dir);
 	return err ? err : list;
 }
-
+#endif
 
 static char osutil_doc[] = "Native operating system services.";
 
diff -r 4e62be0208d3 -r e7a4e81078f5 setup.py
--- a/setup.py	Fri Sep 05 11:04:36 2008 +0200
+++ b/setup.py	Fri Sep 05 09:51:44 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