add eina_file_direct_ls()

Similar to eina_file_ls(), but useful when one wants to apply some
filters to the names before using them as it provides sizes of the
whole path and basename, index of the basename inside the path, dirent
structure for reference (skip directories, etc) and last but not
least, it is faster as it does not stringshare or even builds the full
path at each iteration, instead it keeps the invariant prefix
untouched and just copy the filename.

All in all this is a faster version of eina_file_ls(), use it as
possible as it will avoid overheads.



SVN revision: 50065
This commit is contained in:
Gustavo Sverzut Barbieri 2010-07-06 15:45:12 +00:00
parent 33892c5fc4
commit ed15713785
2 changed files with 167 additions and 7 deletions

View File

@ -22,6 +22,7 @@
#include "eina_types.h"
#include "eina_array.h"
#include "eina_iterator.h"
#include <dirent.h>
/**
* @addtogroup Eina_Tools_Group Tools
@ -55,6 +56,19 @@ EAPI Eina_Bool eina_file_dir_list(const char *dir, Eina_Bool recursive, Eina_Fil
EAPI Eina_Array *eina_file_split(char *path) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1);
EAPI Eina_Iterator *eina_file_ls(const char *dir);
typedef struct _Eina_File_Direct_Info Eina_File_Direct_Info;
struct _Eina_File_Direct_Info
{
size_t path_length; /* size of the whole path */
size_t name_length; /* size of the filename/basename component */
size_t name_start; /* where the filename/basename component starts */
char path[PATH_MAX];
const struct dirent *dirent;
};
EAPI Eina_Iterator *eina_file_direct_ls(const char *dir);
/**
* @}
*/

View File

@ -92,8 +92,9 @@ _eina_file_ls_iterator_next(Eina_File_Iterator *it, void **data)
dp = readdir(it->dirp);
if (!dp) return EINA_FALSE;
}
while (!strcmp(dp->d_name, ".")
|| !strcmp(dp->d_name, ".."));
while ((dp->d_name[0] == '.') &&
((dp->d_name[1] == '\0') ||
((dp->d_name[1] == '.') && (dp->d_name[2] == '\0'))));
length = strlen(dp->d_name);
name = alloca(length + 2 + it->length);
@ -121,6 +122,63 @@ _eina_file_ls_iterator_free(Eina_File_Iterator *it)
free(it);
}
typedef struct _Eina_File_Direct_Iterator Eina_File_Direct_Iterator;
struct _Eina_File_Direct_Iterator
{
Eina_Iterator iterator;
DIR *dirp;
int length;
Eina_File_Direct_Info info;
char dir[1];
};
static Eina_Bool
_eina_file_direct_ls_iterator_next(Eina_File_Direct_Iterator *it, void **data)
{
struct dirent *dp;
size_t length;
do
{
dp = readdir(it->dirp);
if (!dp) return EINA_FALSE;
length = strlen(dp->d_name);
if (it->info.name_start + length + 1 >= PATH_MAX)
continue;
}
while ((dp->d_name[0] == '.') &&
((dp->d_name[1] == '\0') ||
((dp->d_name[1] == '.') && (dp->d_name[2] == '\0'))));
memcpy(it->info.path + it->info.name_start, dp->d_name, length);
it->info.name_length = length;
it->info.path_length = it->info.name_start + length;
it->info.path[it->info.path_length] = '\0';
it->info.dirent = dp;
*data = &it->info;
return EINA_TRUE;
}
static char *
_eina_file_direct_ls_iterator_container(Eina_File_Direct_Iterator *it)
{
return it->dir;
}
static void
_eina_file_direct_ls_iterator_free(Eina_File_Direct_Iterator *it)
{
closedir(it->dirp);
EINA_MAGIC_SET(&it->iterator, 0);
free(it);
}
/*============================================================================*
* Global *
*============================================================================*/
@ -322,12 +380,29 @@ eina_file_split(char *path)
}
/**
* Get an iterator to list the content of a directory. Give a chance to interrupt it
* and make it completly asynchrone.
* Get an iterator to list the content of a directory.
*
* Iterators are cheap to be created and allow interruption at any
* iteration. At each iteration, only the next directory entry is read
* from the filesystem with readdir().
*
* The iterator will handle the user a stringshared value with the
* full path. One must call eina_stringshare_del() on it after usage
* to not leak!
*
* The eina_file_direct_ls() function will provide a possibly faster
* alternative if you need to filter the results somehow, like
* checking extension.
*
* The iterator will walk over '.' and '..' without returning them.
*
* @param dir The name of the directory to list
* @return Return an Eina_Iterator that will walk over the files and directory in the pointed
* directory. On failure it will return NULL.
* @return Return an Eina_Iterator that will walk over the files and
* directory in the pointed directory. On failure it will
* return NULL. The iterator emits stringshared value with the
* full path and must be freed with eina_stringshare_del().
*
* @see eina_file_direct_ls()
*/
EAPI Eina_Iterator *
eina_file_ls(const char *dir)
@ -338,6 +413,7 @@ eina_file_ls(const char *dir)
if (!dir) return NULL;
length = strlen(dir);
if (length < 1) return NULL;
it = malloc(sizeof (Eina_File_Iterator) + length);
if (!it) return NULL;
@ -352,7 +428,10 @@ eina_file_ls(const char *dir)
}
memcpy(it->dir, dir, length + 1);
it->length = length;
if (dir[length - 1] != '/')
it->length = length;
else
it->length = length - 1;
it->iterator.next = FUNC_ITERATOR_NEXT(_eina_file_ls_iterator_next);
it->iterator.get_container = FUNC_ITERATOR_GET_CONTAINER(_eina_file_ls_iterator_container);
@ -361,6 +440,73 @@ eina_file_ls(const char *dir)
return &it->iterator;
}
/**
* Get an iterator to list the content of a directory, with direct information.
*
* Iterators are cheap to be created and allow interruption at any
* iteration. At each iteration, only the next directory entry is read
* from the filesystem with readdir().
*
* The iterator returns the direct pointer to couple of useful information in
* #Eina_File_Direct_Info and that pointer should not be modified anyhow!
*
* The iterator will walk over '.' and '..' without returning them.
*
* @param dir The name of the directory to list
* @return Return an Eina_Iterator that will walk over the files and
* directory in the pointed directory. On failure it will
* return NULL. The iterator emits #Eina_File_Direct_Info
* pointers that could be used but not modified. The lifetime
* of the returned pointer is until the next iteration and
* while the iterator is live, deleting the iterator
* invalidates the pointer.
*
* @see eina_file_ls()
*/
EAPI Eina_Iterator *
eina_file_direct_ls(const char *dir)
{
Eina_File_Direct_Iterator *it;
size_t length;
if (!dir) return NULL;
length = strlen(dir);
if (length < 1) return NULL;
if (length + NAME_MAX + 2 >= PATH_MAX) return NULL;
it = malloc(sizeof(Eina_File_Direct_Iterator) + length);
if (!it) return NULL;
EINA_MAGIC_SET(&it->iterator, EINA_MAGIC_ITERATOR);
it->dirp = opendir(dir);
if (!it->dirp)
{
free(it);
return NULL;
}
memcpy(it->dir, dir, length + 1);
it->length = length;
memcpy(it->info.path, dir, length);
if (dir[length - 1] == '/')
it->info.name_start = length;
else
{
it->info.path[length] = '/';
it->info.name_start = length + 1;
}
it->iterator.next = FUNC_ITERATOR_NEXT(_eina_file_direct_ls_iterator_next);
it->iterator.get_container = FUNC_ITERATOR_GET_CONTAINER(_eina_file_direct_ls_iterator_container);
it->iterator.free = FUNC_ITERATOR_FREE(_eina_file_direct_ls_iterator_free);
return &it->iterator;
}
/**
* @}
*/