Make get_long_path more efficient

Only do filesystem I/O once per call in the common case of paths of less
that 4096 chars
This commit is contained in:
Kovid Goyal 2020-10-30 21:56:28 +05:30
parent b7e6b8fbee
commit a13dd062f3
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
2 changed files with 17 additions and 10 deletions

View File

@ -33,6 +33,7 @@ class wchar_raii {
public: public:
wchar_raii() : handle(NULL) {} wchar_raii() : handle(NULL) {}
wchar_raii(wchar_t *h) : handle(h) {}
~wchar_raii() { ~wchar_raii() {
if (handle) { if (handle) {

View File

@ -770,18 +770,24 @@ static PyObject *
get_long_path_name(PyObject *self, PyObject *args) { get_long_path_name(PyObject *self, PyObject *args) {
wchar_raii path; wchar_raii path;
if (!PyArg_ParseTuple(args, "O&", py_to_wchar_no_none, &path)) return NULL; if (!PyArg_ParseTuple(args, "O&", py_to_wchar_no_none, &path)) return NULL;
DWORD sz = GetLongPathNameW(path.ptr(), NULL, 0) * 2; DWORD current_size = 4096;
if (!sz) return PyErr_SetExcFromWindowsErrWithFilenameObject(PyExc_OSError, 0, PyTuple_GET_ITEM(args, 0)); wchar_raii buf((wchar_t*)PyMem_Malloc(current_size * sizeof(wchar_t)));
wchar_t *buf = (wchar_t*) PyMem_Malloc(sz);
if (!buf) return PyErr_NoMemory(); if (!buf) return PyErr_NoMemory();
if (!GetLongPathNameW(path.ptr(), buf, sz-1)) { DWORD needed_size = GetLongPathNameW(path.ptr(), buf.ptr(), current_size);
PyMem_Free(buf); if (needed_size >= current_size - 32) {
return PyErr_SetExcFromWindowsErrWithFilenameObject(PyExc_OSError, 0, PyTuple_GET_ITEM(args, 0)); current_size = needed_size + 32;
PyMem_Free(buf.ptr());
buf.set_ptr((wchar_t*)PyMem_Malloc(current_size * sizeof(wchar_t)));
if (!buf) return PyErr_NoMemory();
needed_size = GetLongPathNameW(path.ptr(), buf.ptr(), current_size);
} }
buf[sz-1] = 0; if (!needed_size) return PyErr_SetExcFromWindowsErrWithFilenameObject(PyExc_OSError, 0, PyTuple_GET_ITEM(args, 0));
PyObject *ans = PyUnicode_FromWideChar(buf, -1); if (needed_size >= current_size - 2) {
PyMem_Free(buf); PyErr_SetString(PyExc_OSError, "filename length changed between calls");
return ans; return NULL;
}
buf.ptr()[current_size-1] = 0;
return PyUnicode_FromWideChar(buf.ptr(), -1);
} }
static PyObject * static PyObject *