Skip to content

Commit

Permalink
cache stat(2) calls
Browse files Browse the repository at this point in the history
When the user requests sorting by size or mtime, do a "soft preload" of
the file list that only calls stat(2) without loading images.
This avoids calling stat(2) repeatedly on the same files when sorting
the file list, and achieves faster startup on slow filesystems.
  • Loading branch information
ncfavier authored and derf committed Jun 30, 2024
1 parent 186dab6 commit 08b269d
Show file tree
Hide file tree
Showing 5 changed files with 67 additions and 52 deletions.
95 changes: 54 additions & 41 deletions src/filelist.c
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ feh_file *feh_file_new(char *filename)
newfile->name = estrdup(s + 1);
else
newfile->name = estrdup(filename);
newfile->size = -1;
newfile->mtime = 0;
newfile->info = NULL;
#ifdef HAVE_LIBEXIF
newfile->ed = NULL;
Expand Down Expand Up @@ -89,7 +91,6 @@ feh_file_info *feh_file_info_new(void)

info->width = 0;
info->height = 0;
info->size = 0;
info->pixels = 0;
info->has_alpha = 0;
info->format = NULL;
Expand Down Expand Up @@ -305,7 +306,7 @@ void delete_rm_files(void)
return;
}

gib_list *feh_file_info_preload(gib_list * list)
gib_list *feh_file_info_preload(gib_list * list, int load_images)
{
gib_list *l;
feh_file *file = NULL;
Expand All @@ -314,20 +315,27 @@ gib_list *feh_file_info_preload(gib_list * list)
for (l = list; l; l = l->next) {
file = FEH_FILE(l->data);
D(("file %p, file->next %p, file->name %s\n", l, l->next, file->name));
if (feh_file_info_load(file, NULL)) {
D(("Failed to load file %p\n", file));
remove_list = gib_list_add_front(remove_list, l);
if (opt.verbose)
feh_display_status('x');
} else if (((unsigned int)file->info->width < opt.min_width)
|| ((unsigned int)file->info->width > opt.max_width)
|| ((unsigned int)file->info->height < opt.min_height)
|| ((unsigned int)file->info->height > opt.max_height)) {
remove_list = gib_list_add_front(remove_list, l);
if (opt.verbose)
feh_display_status('s');
} else if (opt.verbose)
feh_display_status('.');
if (load_images) {
if (feh_file_info_load(file, NULL)) {
D(("Failed to load file %p\n", file));
remove_list = gib_list_add_front(remove_list, l);
if (opt.verbose)
feh_display_status('x');
} else if (((unsigned int)file->info->width < opt.min_width)
|| ((unsigned int)file->info->width > opt.max_width)
|| ((unsigned int)file->info->height < opt.min_height)
|| ((unsigned int)file->info->height > opt.max_height)) {
remove_list = gib_list_add_front(remove_list, l);
if (opt.verbose)
feh_display_status('s');
} else if (opt.verbose)
feh_display_status('.');
} else {
if (feh_file_stat(file)) {
D(("Failed to stat file %p\n", file));
remove_list = gib_list_add_front(remove_list, l);
}
}
if (sig_exit) {
feh_display_status(0);
exit(sig_exit);
Expand All @@ -348,23 +356,36 @@ gib_list *feh_file_info_preload(gib_list * list)
return(list);
}

int feh_file_info_load(feh_file * file, Imlib_Image im)
int feh_file_stat(feh_file * file)
{
struct stat st;

errno = 0;
if (stat(file->filename, &st)) {
feh_print_stat_error(file->filename);
return(1);
}

file->mtime = st.st_mtime;

file->size = st.st_size;

return(0);
}

int feh_file_info_load(feh_file * file, Imlib_Image im)
{
int need_free = 1;
Imlib_Image im1;

if (feh_file_stat(file))
return(1);

D(("im is %p\n", im));

if (im)
need_free = 0;

errno = 0;
if (stat(file->filename, &st)) {
feh_print_stat_error(file->filename);
return(1);
}

if (im)
im1 = im;
else if (!feh_load_image(&im1, file) || !im1)
Expand All @@ -381,8 +402,6 @@ int feh_file_info_load(feh_file * file, Imlib_Image im)

file->info->format = estrdup(gib_imlib_image_format(im1));

file->info->size = st.st_size;

if (need_free)
gib_imlib_free_image_and_decache(im1);
return(0);
Expand Down Expand Up @@ -434,20 +453,8 @@ int feh_cmp_dirname(void *file1, void *file2)
/* Return -1 if file1 is _newer_ than file2 */
int feh_cmp_mtime(void *file1, void *file2)
{
struct stat s1, s2;

if (stat(FEH_FILE(file1)->filename, &s1)) {
feh_print_stat_error(FEH_FILE(file1)->filename);
return(-1);
}

if (stat(FEH_FILE(file2)->filename, &s2)) {
feh_print_stat_error(FEH_FILE(file2)->filename);
return(-1);
}

/* gib_list_sort is not stable, so explicitly return 0 as -1 */
return(s1.st_mtime >= s2.st_mtime ? -1 : 1);
return(FEH_FILE(file1)->mtime >= FEH_FILE(file2)->mtime ? -1 : 1);
}

int feh_cmp_width(void *file1, void *file2)
Expand All @@ -467,7 +474,7 @@ int feh_cmp_pixels(void *file1, void *file2)

int feh_cmp_size(void *file1, void *file2)
{
return((FEH_FILE(file1)->info->size - FEH_FILE(file2)->info->size));
return((FEH_FILE(file1)->size - FEH_FILE(file2)->size));
}

int feh_cmp_format(void *file1, void *file2)
Expand All @@ -486,10 +493,16 @@ void feh_prepare_filelist(void)
* is set and we're in thumbnail mode, we need to filter images first so
* we can create a properly sized thumbnail list.
*/
if (opt.list || opt.preload || opt.customlist || (opt.sort > SORT_MTIME)
if (opt.list || opt.preload || opt.customlist || (opt.sort >= SORT_WIDTH)
|| (opt.filter_by_dimensions && (opt.index || opt.thumbs || opt.bgmode))) {
/* For these sort options, we have to preload images */
filelist = feh_file_info_preload(filelist);
filelist = feh_file_info_preload(filelist, TRUE);
if (!gib_list_length(filelist))
show_mini_usage();
} else if (opt.sort >= SORT_SIZE) {
/* For these sort options, we need stat(2) information on the files,
* but there is no need to load the images. */
filelist = feh_file_info_preload(filelist, FALSE);
if (!gib_list_length(filelist))
show_mini_usage();
}
Expand Down
10 changes: 6 additions & 4 deletions src/filelist.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ struct __feh_file {
char *name;

/* info stuff */
time_t mtime;
int size;
feh_file_info *info; /* only set when needed */
#ifdef HAVE_LIBEXIF
ExifData *ed;
Expand All @@ -45,7 +47,6 @@ struct __feh_file {
struct __feh_file_info {
int width;
int height;
int size;
int pixels;
unsigned char has_alpha;
char *format;
Expand All @@ -71,11 +72,11 @@ enum sort_type {
SORT_NAME,
SORT_FILENAME,
SORT_DIRNAME,
SORT_SIZE, // everything after SORT_SIZE requires stat(2) information on the filelist
SORT_MTIME,
SORT_WIDTH,
SORT_WIDTH, // everything after SORT_WIDTH requires preloading the images in the filelist
SORT_HEIGHT,
SORT_PIXELS,
SORT_SIZE,
SORT_FORMAT
};

Expand All @@ -88,7 +89,8 @@ int file_selector_all(const struct dirent *unused);
void add_file_to_filelist_recursively(char *origpath, unsigned char level);
void add_file_to_rm_filelist(char *file);
void delete_rm_files(void);
gib_list *feh_file_info_preload(gib_list * list);
gib_list *feh_file_info_preload(gib_list * list, int load_images);
int feh_file_stat(feh_file * file);
int feh_file_info_load(feh_file * file, Imlib_Image im);
void feh_file_dirname(char *dst, feh_file * f, int maxlen);
void feh_prepare_filelist(void);
Expand Down
2 changes: 1 addition & 1 deletion src/list.c
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ void init_list_mode(void)
file->info->height,
format_size(file->info->pixels));
printf("\t%s\t%c\t%s\n",
format_size(file->info->size),
format_size(file->size),
file->info->has_alpha ? 'X' : '-', file->filename);
}

Expand Down
4 changes: 2 additions & 2 deletions src/menu.c
Original file line number Diff line number Diff line change
Expand Up @@ -835,7 +835,7 @@ void feh_menu_draw_submenu_at(int x, int y, Imlib_Image dst, int ox, int oy)
for (int i= 0; i <= 3; i++) {
imlib_image_draw_line(x+i, y+3+i, x+i, y+9-i, 0);
}

return;
}

Expand Down Expand Up @@ -1401,7 +1401,7 @@ static feh_menu *feh_menu_func_gen_info(feh_menu * m)
if (!file->info)
feh_file_info_load(file, im);
if (file->info) {
snprintf(buffer, sizeof(buffer), "Size: %dKb", file->info->size / 1024);
snprintf(buffer, sizeof(buffer), "Size: %dKb", file->size / 1024);
feh_menu_add_entry(mm, buffer, NULL, 0, 0, NULL);
snprintf(buffer, sizeof(buffer), "Dimensions: %dx%d", file->info->width, file->info->height);
feh_menu_add_entry(mm, buffer, NULL, 0, 0, NULL);
Expand Down
8 changes: 4 additions & 4 deletions src/slideshow.c
Original file line number Diff line number Diff line change
Expand Up @@ -496,14 +496,14 @@ char *feh_printf(char *str, feh_file * file, winwidget winwid)
}
break;
case 's':
if (file && (file->info || !feh_file_info_load(file, NULL))) {
snprintf(buf, sizeof(buf), "%d", file->info->size);
if (file && (file->size >= 0 || !feh_file_stat(file))) {
snprintf(buf, sizeof(buf), "%d", file->size);
strncat(ret, buf, sizeof(ret) - strlen(ret) - 1);
}
break;
case 'S':
if (file && (file->info || !feh_file_info_load(file, NULL))) {
strncat(ret, format_size(file->info->size), sizeof(ret) - strlen(ret) - 1);
if (file && (file->size >= 0 || !feh_file_stat(file))) {
strncat(ret, format_size(file->size), sizeof(ret) - strlen(ret) - 1);
}
break;
case 't':
Expand Down

0 comments on commit 08b269d

Please sign in to comment.