directory-files cleanup and speed tweaking

* src/dired.c (directory_files_internal):
Check ‘match’ before doing anything heavyweight.
Move decls closer to use.
Remove obsolete comments about GC.
No need to encode ‘directory’ or to call multibyte_chars_in_text.
Remove no-longer-needed bug check.
Skip finalname construction if file_attributes fails.
This commit is contained in:
Paul Eggert 2019-09-16 13:54:57 -07:00
parent 1a84d8fba4
commit 2335704fcc

View file

@ -167,28 +167,19 @@ directory_files_internal (Lisp_Object directory, Lisp_Object full,
Lisp_Object match, Lisp_Object nosort, bool attrs,
Lisp_Object id_format)
{
ptrdiff_t directory_nbytes;
Lisp_Object list, dirfilename, encoded_directory;
bool needsep = 0;
ptrdiff_t count = SPECPDL_INDEX ();
#ifdef WINDOWSNT
Lisp_Object w32_save = Qnil;
#endif
if (!NILP (match))
CHECK_STRING (match);
/* Don't let the compiler optimize away all copies of DIRECTORY,
which would break GC; see Bug#16986. */
Lisp_Object volatile directory_volatile = directory;
/* Because of file name handlers, these functions might call
Ffuncall, and cause a GC. */
list = encoded_directory = dirfilename = Qnil;
dirfilename = Fdirectory_file_name (directory);
Lisp_Object dirfilename = Fdirectory_file_name (directory);
/* Note: ENCODE_FILE and DECODE_FILE can GC because they can run
run_pre_post_conversion_on_str which calls Lisp directly and
indirectly. */
Lisp_Object encoded_dirfilename = ENCODE_FILE (dirfilename);
encoded_directory = ENCODE_FILE (directory);
int fd;
DIR *d = open_directory (dirfilename, encoded_dirfilename, &fd);
@ -196,9 +187,11 @@ directory_files_internal (Lisp_Object directory, Lisp_Object full,
/* Unfortunately, we can now invoke expand-file-name and
file-attributes on filenames, both of which can throw, so we must
do a proper unwind-protect. */
ptrdiff_t count = SPECPDL_INDEX ();
record_unwind_protect_ptr (directory_files_internal_unwind, d);
#ifdef WINDOWSNT
Lisp_Object w32_save = Qnil;
if (attrs)
{
/* Do this only once to avoid doing it (in w32.c:stat) for each
@ -218,89 +211,63 @@ directory_files_internal (Lisp_Object directory, Lisp_Object full,
}
#endif
directory_nbytes = SBYTES (directory);
ptrdiff_t directory_nbytes = SBYTES (directory);
re_match_object = Qt;
/* Decide whether we need to add a directory separator. */
if (directory_nbytes == 0
|| !IS_ANY_SEP (SREF (directory, directory_nbytes - 1)))
needsep = 1;
bool needsep = (directory_nbytes == 0
|| !IS_ANY_SEP (SREF (directory, directory_nbytes - 1)));
/* Windows users want case-insensitive wildcards. */
Lisp_Object case_table =
Lisp_Object case_table = Qnil;
#ifdef WINDOWSNT
BVAR (&buffer_defaults, case_canon_table)
#else
Qnil
case_table = BVAR (&buffer_defaults, case_canon_table);
#endif
;
if (!NILP (match))
CHECK_STRING (match);
/* Loop reading directory entries. */
/* Read directory entries and accumulate them into LIST. */
Lisp_Object list = Qnil;
for (struct dirent *dp; (dp = read_dirent (d, directory)); )
{
ptrdiff_t len = dirent_namelen (dp);
Lisp_Object name = make_unibyte_string (dp->d_name, len);
Lisp_Object finalname = name;
/* Note: DECODE_FILE can GC; it should protect its argument,
though. */
/* This can GC. */
name = DECODE_FILE (name);
len = SBYTES (name);
/* Now that we have unwind_protect in place, we might as well
allow matching to be interrupted. */
maybe_quit ();
bool wanted = (NILP (match) ||
fast_string_match_internal (
match, name, case_table) >= 0);
if (!NILP (match)
&& fast_string_match_internal (match, name, case_table) < 0)
continue;
if (wanted)
Lisp_Object fileattrs;
if (attrs)
{
if (!NILP (full))
{
Lisp_Object fullname;
ptrdiff_t nbytes = len + directory_nbytes + needsep;
ptrdiff_t nchars;
fullname = make_uninit_multibyte_string (nbytes, nbytes);
memcpy (SDATA (fullname), SDATA (directory),
directory_nbytes);
if (needsep)
SSET (fullname, directory_nbytes, DIRECTORY_SEP);
memcpy (SDATA (fullname) + directory_nbytes + needsep,
SDATA (name), len);
nchars = multibyte_chars_in_text (SDATA (fullname), nbytes);
/* Some bug somewhere. */
if (nchars > nbytes)
emacs_abort ();
STRING_SET_CHARS (fullname, nchars);
if (nchars == nbytes)
STRING_SET_UNIBYTE (fullname);
finalname = fullname;
}
else
finalname = name;
if (attrs)
{
Lisp_Object fileattrs
= file_attributes (fd, dp->d_name, directory, name, id_format);
if (!NILP (fileattrs))
list = Fcons (Fcons (finalname, fileattrs), list);
}
else
list = Fcons (finalname, list);
fileattrs = file_attributes (fd, dp->d_name, directory, name,
id_format);
if (NILP (fileattrs))
continue;
}
if (!NILP (full))
{
ptrdiff_t name_nbytes = SBYTES (name);
ptrdiff_t nbytes = directory_nbytes + needsep + name_nbytes;
ptrdiff_t nchars = SCHARS (directory) + needsep + SCHARS (name);
finalname = make_uninit_multibyte_string (nchars, nbytes);
if (nchars == nbytes)
STRING_SET_UNIBYTE (finalname);
memcpy (SDATA (finalname), SDATA (directory), directory_nbytes);
if (needsep)
SSET (finalname, directory_nbytes, DIRECTORY_SEP);
memcpy (SDATA (finalname) + directory_nbytes + needsep,
SDATA (name), name_nbytes);
}
else
finalname = name;
list = Fcons (attrs ? Fcons (finalname, fileattrs) : finalname, list);
}
closedir (d);
@ -330,14 +297,14 @@ If MATCH is non-nil, mention only file names that match the regexp MATCH.
If NOSORT is non-nil, the list is not sorted--its order is unpredictable.
Otherwise, the list returned is sorted with `string-lessp'.
NOSORT is useful if you plan to sort the result yourself. */)
(Lisp_Object directory, Lisp_Object full, Lisp_Object match, Lisp_Object nosort)
(Lisp_Object directory, Lisp_Object full, Lisp_Object match,
Lisp_Object nosort)
{
Lisp_Object handler;
directory = Fexpand_file_name (directory, Qnil);
/* If the file name has special constructs in it,
call the corresponding file name handler. */
handler = Ffind_file_name_handler (directory, Qdirectory_files);
Lisp_Object handler = Ffind_file_name_handler (directory, Qdirectory_files);
if (!NILP (handler))
return call5 (handler, Qdirectory_files, directory,
full, match, nosort);
@ -365,14 +332,15 @@ ID-FORMAT specifies the preferred format of attributes uid and gid, see
`file-attributes' for further documentation.
On MS-Windows, performance depends on `w32-get-true-file-attributes',
which see. */)
(Lisp_Object directory, Lisp_Object full, Lisp_Object match, Lisp_Object nosort, Lisp_Object id_format)
(Lisp_Object directory, Lisp_Object full, Lisp_Object match,
Lisp_Object nosort, Lisp_Object id_format)
{
Lisp_Object handler;
directory = Fexpand_file_name (directory, Qnil);
/* If the file name has special constructs in it,
call the corresponding file name handler. */
handler = Ffind_file_name_handler (directory, Qdirectory_files_and_attributes);
Lisp_Object handler
= Ffind_file_name_handler (directory, Qdirectory_files_and_attributes);
if (!NILP (handler))
return call6 (handler, Qdirectory_files_and_attributes,
directory, full, match, nosort, id_format);
@ -1032,7 +1000,8 @@ file_attributes (int fd, char const *name,
INT_TO_INTEGER (s.st_dev));
}
DEFUN ("file-attributes-lessp", Ffile_attributes_lessp, Sfile_attributes_lessp, 2, 2, 0,
DEFUN ("file-attributes-lessp", Ffile_attributes_lessp,
Sfile_attributes_lessp, 2, 2, 0,
doc: /* Return t if first arg file attributes list is less than second.
Comparison is in lexicographic order and case is significant. */)
(Lisp_Object f1, Lisp_Object f2)