Refactor directory listing to greatly optimize sorting.

This commit is contained in:
Themaister 2012-06-23 15:31:22 +02:00
parent 031e37db9c
commit 14f7f641f3
5 changed files with 140 additions and 125 deletions

View File

@ -484,28 +484,31 @@ static void deinit_filter(void)
#endif
#ifdef HAVE_XML
static void deinit_shader_dir(void)
{
// It handles NULL, no worries :D
dir_list_free(g_extern.shader_dir.list);
g_extern.shader_dir.list = NULL;
g_extern.shader_dir.ptr = 0;
}
static void init_shader_dir(void)
{
if (!*g_settings.video.shader_dir)
return;
g_extern.shader_dir.elems = dir_list_new(g_settings.video.shader_dir, "shader", false);
g_extern.shader_dir.size = dir_list_size(g_extern.shader_dir.elems);
g_extern.shader_dir.ptr = 0;
g_extern.shader_dir.list = dir_list_new(g_settings.video.shader_dir, "shader", false);
if (g_extern.shader_dir.list->size == 0)
{
deinit_shader_dir();
return;
}
dir_list_sort(g_extern.shader_dir.elems, false);
g_extern.shader_dir.ptr = 0;
dir_list_sort(g_extern.shader_dir.list, false);
for (unsigned i = 0; i < g_extern.shader_dir.size; i++)
RARCH_LOG("Found shader \"%s\"\n", g_extern.shader_dir.elems[i]);
}
static void deinit_shader_dir(void)
{
// It handles NULL, no worries :D
dir_list_free(g_extern.shader_dir.elems);
g_extern.shader_dir.elems = NULL;
g_extern.shader_dir.size = 0;
g_extern.shader_dir.ptr = 0;
for (unsigned i = 0; i < g_extern.shader_dir.list->size; i++)
RARCH_LOG("Found shader \"%s\"\n", g_extern.shader_dir.list->elems[i].data);
}
#endif

31
file.h
View File

@ -36,13 +36,30 @@ void save_ram_file(const char *path, int type);
bool init_rom_file(enum rarch_game_type type);
// Returns a NULL-terminated list of files in a directory with full paths.
// If ext is NULL, any file will be picked.
// If non-NULL, only files with extension ext are added.
char **dir_list_new(const char *dir, const char *ext, bool include_dirs);
size_t dir_list_size(char * const *dir_list);
void dir_list_sort(char **dir_list, bool dir_first);
void dir_list_free(char **dir_list);
// Yep, this is C alright ;)
union string_list_elem_attr
{
bool b;
int i;
void *p;
};
struct string_list_elem
{
char *data;
union string_list_elem_attr attr;
};
struct string_list
{
struct string_list_elem *elems;
size_t size;
size_t cap;
};
struct string_list *dir_list_new(const char *dir, const char *ext, bool include_dirs);
void dir_list_sort(struct string_list *list, bool dir_first);
void dir_list_free(struct string_list *list);
bool path_is_directory(const char *path);
bool path_file_exists(const char *path);

View File

@ -43,111 +43,105 @@
#include <dirent.h>
#endif
// Yep, this is C alright ;)
struct string_list
static void string_list_free(struct string_list *list)
{
char **data;
size_t size;
size_t cap;
};
if (!list)
return;
for (size_t i = 0; i < list->size; i++)
free(list->elems[i].data);
free(list->elems);
free(list);
}
static bool string_list_capacity(struct string_list *list, size_t cap)
{
rarch_assert(cap > list->size);
char **new_data = (char**)realloc(list->data, cap * sizeof(char*));
struct string_list_elem *new_data = (struct string_list_elem*)realloc(list->elems, cap * sizeof(*new_data));
if (!new_data)
return false;
list->data = new_data;
list->cap = cap;
list->elems = new_data;
list->cap = cap;
return true;
}
static bool string_list_init(struct string_list *list)
static struct string_list *string_list_new(void)
{
memset(list, 0, sizeof(*list));
return string_list_capacity(list, 32);
struct string_list *list = (struct string_list*)calloc(1, sizeof(*list));
if (!list)
return NULL;
if (!string_list_capacity(list, 32))
{
string_list_free(list);
return NULL;
}
return list;
}
static bool string_list_append(struct string_list *list, const char *elem)
static bool string_list_append(struct string_list *list, const char *elem, union string_list_elem_attr attr)
{
if (list->size + 1 >= list->cap && !string_list_capacity(list, list->cap * 2))
if (list->size >= list->cap &&
!string_list_capacity(list, list->cap * 2))
return false;
if (!(list->data[list->size] = strdup(elem)))
char *dup = strdup(elem);
if (!dup)
return false;
list->elems[list->size].data = dup;
list->elems[list->size].attr = attr;
list->size++;
return true;
}
static char **string_list_finalize(struct string_list *list)
{
rarch_assert(list->cap > list->size);
list->data[list->size] = NULL;
return list->data;
}
static void string_list_cleanup(struct string_list *list)
{
for (size_t i = 0; i < list->size; i++)
free(list->data[i]);
free(list->data);
memset(list, 0, sizeof(*list));
}
static void string_list_free(char **list)
{
if (!list)
return;
char **orig = list;
while (*list)
free(*list++);
free(orig);
}
static char **string_split(const char *str, const char *delim)
static struct string_list *string_split(const char *str, const char *delim)
{
char *copy = NULL;
const char *tmp = NULL;
struct string_list list;
if (!string_list_init(&list))
struct string_list *list = string_list_new();
if (!list)
goto error;
copy = strdup(str);
if (!copy)
return NULL;
goto error;
tmp = strtok(copy, delim);
while (tmp)
{
if (!string_list_append(&list, tmp))
union string_list_elem_attr attr;
memset(&attr, 0, sizeof(attr));
if (!string_list_append(list, tmp, attr))
goto error;
tmp = strtok(NULL, delim);
}
free(copy);
return string_list_finalize(&list);
return list;
error:
string_list_cleanup(&list);
string_list_free(list);
free(copy);
return NULL;
}
static bool string_list_find_elem(char * const *list, const char *elem)
static bool string_list_find_elem(const struct string_list *list, const char *elem)
{
if (!list)
return false;
for (; *list; list++)
if (strcmp(*list, elem) == 0)
for (size_t i = 0; i < list->size; i++)
{
if (strcmp(list->elems[i].data, elem) == 0)
return true;
}
return false;
}
@ -161,50 +155,42 @@ static const char *path_get_extension(const char *path)
return "";
}
size_t dir_list_size(char * const *dir_list)
static int qstrcmp_plain(const void *a_, const void *b_)
{
if (!dir_list)
return 0;
const struct string_list_elem *a = (const struct string_list_elem*)a_;
const struct string_list_elem *b = (const struct string_list_elem*)b_;
size_t size = 0;
while (*dir_list++)
size++;
return size;
}
static int qstrcmp_plain(const void *a, const void *b)
{
return strcasecmp(*(const char * const*)a, *(const char * const*)b);
return strcasecmp(a->data, b->data);
}
static int qstrcmp_dir(const void *a_, const void *b_)
{
const char *a = *(const char * const*)a_;
const char *b = *(const char * const*)b_;
const struct string_list_elem *a = (const struct string_list_elem*)a_;
const struct string_list_elem *b = (const struct string_list_elem*)b_;
// Sort directories before files.
int a_dir = path_is_directory(a);
int b_dir = path_is_directory(b);
int a_dir = a->attr.b;
int b_dir = b->attr.b;
if (a_dir != b_dir)
return b_dir - a_dir;
return strcasecmp(a, b);
else
return strcasecmp(a->data, b->data);
}
void dir_list_sort(char **dir_list, bool dir_first)
void dir_list_sort(struct string_list *list, bool dir_first)
{
if (!dir_list)
if (!list)
return;
qsort(dir_list, dir_list_size(dir_list), sizeof(char*), dir_first ? qstrcmp_dir : qstrcmp_plain);
qsort(list->elems, list->size, sizeof(struct string_list_elem),
dir_first ? qstrcmp_dir : qstrcmp_plain);
}
#ifdef _WIN32 // Because the API is just fucked up ...
char **dir_list_new(const char *dir, const char *ext, bool include_dirs)
struct string_list *dir_list_new(const char *dir, const char *ext, bool include_dirs)
{
struct string_list list;
if (!string_list_init(&list))
struct string_list *list = string_list_new();
if (!list)
return NULL;
HANDLE hFind = INVALID_HANDLE_VALUE;
@ -213,7 +199,7 @@ char **dir_list_new(const char *dir, const char *ext, bool include_dirs)
char path_buf[PATH_MAX];
snprintf(path_buf, sizeof(path_buf), "%s\\*", dir);
char **ext_list = NULL;
struct string_list *ext_list = NULL;
if (ext)
ext_list = string_split(ext, "|");
@ -236,35 +222,38 @@ char **dir_list_new(const char *dir, const char *ext, bool include_dirs)
char file_path[PATH_MAX];
snprintf(file_path, sizeof(file_path), "%s\\%s", dir, name);
if (!string_list_append(&list, file_path))
union string_list_elem_attr attr;
attr.b = is_dir;
if (!string_list_append(list, file_path, attr))
goto error;
}
while (FindNextFile(hFind, &ffd) != 0);
FindClose(hFind);
string_list_free(ext_list);
return string_list_finalize(&list);
return list;
error:
RARCH_ERR("Failed to open directory: \"%s\"\n", dir);
if (hFind != INVALID_HANDLE_VALUE)
FindClose(hFind);
string_list_cleanup(&list);
string_list_free(list);
string_list_free(ext_list);
return NULL;
}
#else
char **dir_list_new(const char *dir, const char *ext, bool include_dirs)
struct string_list *dir_list_new(const char *dir, const char *ext, bool include_dirs)
{
struct string_list list;
if (!string_list_init(&list))
struct string_list *list = string_list_new();
if (!list)
return NULL;
DIR *directory = NULL;
const struct dirent *entry = NULL;
char **ext_list = NULL;
struct string_list *ext_list = NULL;
if (ext)
ext_list = string_split(ext, "|");
@ -287,14 +276,17 @@ char **dir_list_new(const char *dir, const char *ext, bool include_dirs)
char file_path[PATH_MAX];
snprintf(file_path, sizeof(file_path), "%s/%s", dir, name);
if (!string_list_append(&list, file_path))
union string_list_elem_attr attr;
attr.b = is_dir;
if (!string_list_append(list, file_path, attr))
goto error;
}
closedir(directory);
string_list_free(ext_list);
return string_list_finalize(&list);
return list;
error:
RARCH_ERR("Failed to open directory: \"%s\"\n", dir);
@ -302,15 +294,15 @@ error:
if (directory)
closedir(directory);
string_list_cleanup(&list);
string_list_free(list);
string_list_free(ext_list);
return NULL;
}
#endif
void dir_list_free(char **dir_list)
void dir_list_free(struct string_list *list)
{
string_list_free(dir_list);
string_list_free(list);
}
bool path_is_directory(const char *path)

View File

@ -449,8 +449,7 @@ struct global
struct
{
char **elems;
size_t size;
struct string_list *list;
size_t ptr;
} shader_dir;

View File

@ -1513,6 +1513,9 @@ static void set_savestate_auto_index(void)
if (!g_settings.savestate_auto_index)
return;
// Find the file in the same directory as g_extern.savestate_name with the largest numeral suffix.
// E.g. /foo/path/game.state, will try to find /foo/path/game.state%d, where %d is the largest number available.
char state_path[PATH_MAX];
strlcpy(state_path, g_extern.savestate_name, sizeof(state_path));
@ -1527,22 +1530,23 @@ static void set_savestate_auto_index(void)
*split = '\0';
base = split + 1;
}
else
dir = ".";
unsigned max_index = 0;
char **dir_list = dir_list_new(dir, NULL, false);
struct string_list *dir_list = dir_list_new(dir, NULL, false);
if (!dir_list)
return;
unsigned index = 0;
const char *dir_elem;
while ((dir_elem = dir_list[index++]))
for (size_t i = 0; i < dir_list->size; i++)
{
if (!strstr(dir_elem, base))
const char *dir_elem = dir_list->elems[i].data;
if (strstr(dir_elem, base) != dir_elem)
continue;
const char *end = dir_elem + strlen(dir_elem);
while ((end != dir_elem) && isdigit(end[-1])) end--;
while ((end > dir_elem) && isdigit(end[-1])) end--;
unsigned index = strtoul(end, NULL, 0);
if (index > max_index)
@ -2128,7 +2132,7 @@ static void check_shader_dir(void)
static bool old_pressed_next = false;
static bool old_pressed_prev = false;
if (!g_extern.shader_dir.elems || !driver.video->xml_shader)
if (!g_extern.shader_dir.list || !driver.video->xml_shader)
return;
bool should_apply = false;
@ -2137,20 +2141,20 @@ static void check_shader_dir(void)
if (pressed_next && !old_pressed_next)
{
should_apply = true;
g_extern.shader_dir.ptr = (g_extern.shader_dir.ptr + 1) % g_extern.shader_dir.size;
g_extern.shader_dir.ptr = (g_extern.shader_dir.ptr + 1) % g_extern.shader_dir.list->size;
}
else if (pressed_prev && !old_pressed_prev)
{
should_apply = true;
if (g_extern.shader_dir.ptr == 0)
g_extern.shader_dir.ptr = g_extern.shader_dir.size - 1;
g_extern.shader_dir.ptr = g_extern.shader_dir.list->size - 1;
else
g_extern.shader_dir.ptr--;
}
if (should_apply)
{
const char *shader = g_extern.shader_dir.elems[g_extern.shader_dir.ptr];
const char *shader = g_extern.shader_dir.list->elems[g_extern.shader_dir.ptr].data;
strlcpy(g_settings.video.bsnes_shader_path, shader, sizeof(g_settings.video.bsnes_shader_path));
g_settings.video.shader_type = RARCH_SHADER_BSNES;