Rewrite dircache generation to take advantage for the FAT code. Reduce RAM usage by ~30Kb and binsize by at least several hundreds bytes. Also remove the directory depth limit of dircache.

git-svn-id: svn://svn.rockbox.org/rockbox/trunk@24657 a1c6a512-1295-4272-9138-f99709370657
This commit is contained in:
Amaury Pouly 2010-02-14 19:09:52 +00:00
parent e1d8c3ef7c
commit 26c0e753d1
2 changed files with 186 additions and 201 deletions

View file

@ -71,7 +71,6 @@ static unsigned long entry_count = 0;
static unsigned long reserve_used = 0;
static unsigned int cache_build_ticks = 0;
static unsigned long appflags = 0;
static char dircache_cur_path[MAX_PATH];
static struct event_queue dircache_queue;
static long dircache_stack[(DEFAULT_STACK_SIZE + 0x900)/sizeof(long)];
@ -147,11 +146,6 @@ static struct dircache_entry* dircache_gen_down(struct dircache_entry *ce)
return next_entry;
}
/* This will eat ~30 KiB of memory!
* We should probably use that as additional reserve buffer in future. */
#define MAX_SCAN_DEPTH 16
static struct travel_data dir_recursion[MAX_SCAN_DEPTH];
/**
* Returns true if there is an event waiting in the queue
* that requires the current operation to be aborted.
@ -176,130 +170,111 @@ static bool check_event_queue(void)
return false;
}
/**
* Internal function to iterate a path.
*/
static int dircache_scan(IF_MV2(int volume,) struct travel_data *td)
#ifndef SIMULATOR
/* scan and build static data (avoid redundancy on stack) */
static struct
{
#ifdef SIMULATOR
#ifdef HAVE_MULTIVOLUME
(void)volume;
#endif
while ( ( td->entry = readdir_uncached(td->dir) ) )
#else
while ( (fat_getnext(td->dir, &td->entry) >= 0) && (td->entry.name[0]))
int volume;
#endif
struct fat_dir *dir;
struct fat_direntry *direntry;
}sab;
static int sab_process_dir(unsigned long startcluster, struct dircache_entry *ce)
{
/* normally, opendir expects a full fat_dir as parent but in our case,
* it's completely useless because we don't modify anything
* WARNING: this heavily relies on current FAT implementation ! */
/* those field are necessary to update the FAT entry in case of modification
here we don't touch anything so we put dummy values */
sab.dir->entry = 0;
sab.dir->entrycount = 0;
sab.dir->file.firstcluster = 0;
/* open directory */
int rc = fat_opendir(IF_MV2(sab.volume,) sab.dir, startcluster, sab.dir);
if(rc < 0)
{
#ifdef SIMULATOR
if (!strcmp(".", td->entry->d_name) ||
!strcmp("..", td->entry->d_name))
{
logf("fat_opendir failed: %d", rc);
return rc;
}
/* first pass : read dir */
struct dircache_entry *first_ce = ce;
/* read through directory */
while((rc = fat_getnext(sab.dir, sab.direntry)) >= 0 && sab.direntry->name[0])
{
if(!strcmp(".", sab.direntry->name) ||
!strcmp("..", sab.direntry->name))
continue;
}
td->ce->attribute = td->entry->attribute;
td->ce->name_len = strlen(td->entry->d_name) + 1;
td->ce->d_name = ((char *)dircache_root+dircache_size);
td->ce->size = td->entry->size;
td->ce->wrtdate = td->entry->wrtdate;
td->ce->wrttime = td->entry->wrttime;
memcpy(td->ce->d_name, td->entry->d_name, td->ce->name_len);
#else
if (!strcmp(".", td->entry.name) ||
!strcmp("..", td->entry.name))
{
continue;
}
ce->attribute = sab.direntry->attr;
ce->name_len = strlen(sab.direntry->name) + 1;
ce->d_name = ((char *)dircache_root + dircache_size);
ce->startcluster = sab.direntry->firstcluster;
ce->size = sab.direntry->filesize;
ce->wrtdate = sab.direntry->wrtdate;
ce->wrttime = sab.direntry->wrttime;
memcpy(ce->d_name, sab.direntry->name, ce->name_len);
td->ce->attribute = td->entry.attr;
td->ce->name_len = strlen(td->entry.name) + 1;
td->ce->d_name = ((char *)dircache_root+dircache_size);
td->ce->startcluster = td->entry.firstcluster;
td->ce->size = td->entry.filesize;
td->ce->wrtdate = td->entry.wrtdate;
td->ce->wrttime = td->entry.wrttime;
memcpy(td->ce->d_name, td->entry.name, td->ce->name_len);
#endif
dircache_size += td->ce->name_len;
dircache_size += ce->name_len;
entry_count++;
#ifdef SIMULATOR
if (td->entry->attribute & ATTR_DIRECTORY)
#else
if (td->entry.attr & FAT_ATTR_DIRECTORY)
#endif
{
td->down_entry = dircache_gen_down(td->ce);
if (td->down_entry == NULL)
return -2;
td->pathpos = strlen(dircache_cur_path);
strlcpy(&dircache_cur_path[td->pathpos], "/",
sizeof(dircache_cur_path) - td->pathpos);
#ifdef SIMULATOR
strlcpy(&dircache_cur_path[td->pathpos+1], td->entry->d_name,
sizeof(dircache_cur_path) - td->pathpos - 1);
td->newdir = opendir_uncached(dircache_cur_path);
if (td->newdir == NULL)
{
logf("Failed to opendir_uncached(): %s", dircache_cur_path);
return -3;
}
#else
strlcpy(&dircache_cur_path[td->pathpos+1], td->entry.name,
sizeof(dircache_cur_path) - td->pathpos - 1);
td->newdir = *td->dir;
if (fat_opendir(IF_MV2(volume,) &td->newdir,
td->entry.firstcluster, td->dir) < 0 )
{
return -3;
}
#endif
td->ce = dircache_gen_next(td->ce);
if (td->ce == NULL)
return -4;
return 1;
}
td->ce->down = NULL;
td->ce = dircache_gen_next(td->ce);
if (td->ce == NULL)
if(ce->attribute & FAT_ATTR_DIRECTORY)
dircache_gen_down(ce);
ce = dircache_gen_next(ce);
if(ce == NULL)
return -5;
/* When simulator is used, it's only safe to yield here. */
if (thread_enabled)
if(thread_enabled)
{
/* Stop if we got an external signal. */
if (check_event_queue())
if(check_event_queue())
return -6;
yield();
}
}
return 0;
/* add "." and ".." */
ce->d_name = ".";
ce->name_len = 2;
ce->attribute = FAT_ATTR_DIRECTORY;
ce->startcluster = startcluster;
ce->size = 0;
ce->down = first_ce;
ce = dircache_gen_next(ce);
ce->d_name = "..";
ce->name_len = 3;
ce->attribute = FAT_ATTR_DIRECTORY;
ce->startcluster = first_ce->up->startcluster;
ce->size = 0;
ce->down = first_ce->up;
/* second pass: recurse ! */
ce = first_ce;
while(ce)
{
if(ce->name_len != 0 && ce->down != NULL && strcmp(ce->d_name, ".") && strcmp(ce->d_name, ".."))
sab_process_dir(ce->startcluster, ce->down);
ce = ce->next;
}
return rc;
}
/**
* Recursively scan the hard disk and build the cache.
*/
#ifdef SIMULATOR
static int dircache_travel(IF_MV2(int volume,) DIR_UNCACHED *dir, struct dircache_entry *ce)
#else
static int dircache_travel(IF_MV2(int volume,) struct fat_dir *dir, struct dircache_entry *ce)
#endif
static int dircache_scan_and_build(IF_MV2(int volume,) struct dircache_entry *ce)
{
int depth = 0;
int result;
memset(ce, 0, sizeof(struct dircache_entry));
#if defined(HAVE_MULTIVOLUME) && !defined(SIMULATOR)
#ifdef HAVE_MULTIVOLUME
if (volume > 0)
{
ce->d_name = ((char *)dircache_root+dircache_size);
@ -313,81 +288,117 @@ static int dircache_travel(IF_MV2(int volume,) struct fat_dir *dir, struct dirca
}
#endif
dir_recursion[0].dir = dir;
dir_recursion[0].ce = ce;
dir_recursion[0].first = ce;
do {
//logf("=> %s", dircache_cur_path);
result = dircache_scan(IF_MV2(volume,) &dir_recursion[depth]);
switch (result) {
case 0: /* Leaving the current directory. */
/* Add the standard . and .. entries. */
ce = dir_recursion[depth].ce;
ce->d_name = ".";
ce->name_len = 2;
#ifdef SIMULATOR
closedir_uncached(dir_recursion[depth].dir);
ce->attribute = ATTR_DIRECTORY;
#else
ce->attribute = FAT_ATTR_DIRECTORY;
ce->startcluster = dir_recursion[depth].dir->file.firstcluster;
struct fat_dir dir; /* allocate on stack once for all scan */
struct fat_direntry direntry; /* ditto */
#ifdef HAVE_MULTIVOLUME
sab.volume = volume;
#endif
ce->size = 0;
ce->down = dir_recursion[depth].first;
sab.dir = &dir;
sab.direntry = &direntry;
depth--;
if (depth < 0)
break ;
return sab_process_dir(0, ce);
}
#else /* !SIMULATOR */
static char sab_path[MAX_PATH];
dircache_cur_path[dir_recursion[depth].pathpos] = '\0';
ce = dircache_gen_next(ce);
if (ce == NULL)
{
logf("memory allocation error");
return -3;
}
#ifdef SIMULATOR
ce->attribute = ATTR_DIRECTORY;
#else
ce->attribute = FAT_ATTR_DIRECTORY;
ce->startcluster = dir_recursion[depth].dir->file.firstcluster;
#endif
ce->d_name = "..";
ce->name_len = 3;
ce->size = 0;
ce->down = dir_recursion[depth].first;
static int sab_process_dir(struct dircache_entry *ce)
{
struct dirent_uncached *entry;
struct dircache_entry *first_ce = ce;
DIR_UNCACHED *dir = opendir_uncached(sab_path);
if(dir == NULL)
{
logf("Failed to opendir_uncached(%s)", sab_path);
return -1;
}
break ;
case 1: /* Going down in the directory tree. */
depth++;
if (depth >= MAX_SCAN_DEPTH)
{
logf("Too deep directory structure");
return -2;
}
#ifdef SIMULATOR
dir_recursion[depth].dir = dir_recursion[depth-1].newdir;
#else
dir_recursion[depth].dir = &dir_recursion[depth-1].newdir;
#endif
dir_recursion[depth].first = dir_recursion[depth-1].down_entry;
dir_recursion[depth].ce = dir_recursion[depth-1].down_entry;
break ;
default:
logf("Scan failed");
logf("-> %s", dircache_cur_path);
while((entry = readdir_uncached(dir)))
{
if(!strcmp(".", entry->d_name) ||
!strcmp("..", entry->d_name))
continue;
ce->attribute = entry->attribute;
ce->name_len = strlen(entry->d_name) + 1;
ce->d_name = ((char *)dircache_root + dircache_size);
ce->size = entry->size;
ce->wrtdate = entry->wrtdate;
ce->wrttime = entry->wrttime;
memcpy(ce->d_name, entry->d_name, ce->name_len);
dircache_size += ce->name_len;
entry_count++;
if(entry->attribute & ATTR_DIRECTORY)
{
dircache_gen_down(ce);
if(ce->down == NULL)
{
closedir_uncached(dir);
return -1;
}
/* save current paths size */
int pathpos = strlen(sab_path);
/* append entry */
strlcpy(&sab_path[pathpos], "/", sizeof(sab_path) - pathpos);
strlcpy(&sab_path[pathpos+1], entry->d_name, sizeof(sab_path) - pathpos - 1);
int rc = sab_process_dir(ce->down);
/* restore path */
sab_path[pathpos] = '\0';
if(rc < 0)
{
closedir_uncached(dir);
return rc;
}
}
} while (depth >= 0) ;
ce = dircache_gen_next(ce);
if(ce == NULL)
return -5;
/* When simulator is used, it's only safe to yield here. */
if(thread_enabled)
{
/* Stop if we got an external signal. */
if(check_event_queue())
return -1;
yield();
}
}
/* add "." and ".." */
ce->d_name = ".";
ce->name_len = 2;
ce->attribute = ATTR_DIRECTORY;
ce->size = 0;
ce->down = first_ce;
ce = dircache_gen_next(ce);
ce->d_name = "..";
ce->name_len = 3;
ce->attribute = ATTR_DIRECTORY;
ce->size = 0;
ce->down = first_ce->up;
closedir_uncached(dir);
return 0;
}
static int dircache_scan_and_build(IF_MV2(int volume,) struct dircache_entry *ce)
{
#ifdef HAVE_MULTIVOLUME
(void) volume;
#endif
memset(ce, 0, sizeof(struct dircache_entry));
strlcpy(sab_path, "/", sizeof sab_path);
return sab_process_dir(ce);
}
#endif /* SIMULATOR */
/**
* Internal function to get a pointer to dircache_entry for a given filename.
* path: Absolute path to a file or directory (see comment)
@ -578,11 +589,6 @@ int dircache_save(void)
*/
static int dircache_do_rebuild(void)
{
#ifdef SIMULATOR
DIR_UNCACHED *pdir;
#else
struct fat_dir dir, *pdir;
#endif
unsigned int start_tick;
int i;
@ -592,7 +598,6 @@ static int dircache_do_rebuild(void)
appflags = 0;
entry_count = 0;
memset(dircache_cur_path, 0, sizeof(dircache_cur_path));
dircache_size = sizeof(struct dircache_entry);
#ifdef HAVE_MULTIVOLUME
@ -602,32 +607,12 @@ static int dircache_do_rebuild(void)
{
if (fat_ismounted(i))
{
#endif
#ifdef SIMULATOR
pdir = opendir_uncached("/");
if (pdir == NULL)
{
logf("Failed to open rootdir");
dircache_initializing = false;
return -3;
}
#else
#ifdef HAVE_MULTIVOLUME
if ( fat_opendir(IF_MV2(i,) &dir, 0, NULL) < 0 ) {
#else
if ( fat_opendir(IF_MV2(0,) &dir, 0, NULL) < 0 ) {
#endif /* HAVE_MULTIVOLUME */
logf("Failed opening root dir");
dircache_initializing = false;
return -3;
}
pdir = &dir;
#endif
cpu_boost(true);
#ifdef HAVE_MULTIVOLUME
if (dircache_travel(IF_MV2(i,) pdir, append_position) < 0)
if (dircache_scan_and_build(IF_MV2(i,) append_position) < 0)
#else
if (dircache_travel(IF_MV2(0,) pdir, dircache_root) < 0)
if (dircache_scan_and_build(IF_MV2(0,) dircache_root) < 0)
#endif /* HAVE_MULTIVOLUME */
{
logf("dircache_travel failed");

View file

@ -122,7 +122,7 @@ extern int fat_rename(struct fat_file* file,
long size, int attr);
extern int fat_opendir(IF_MV2(int volume,)
struct fat_dir *ent, unsigned long currdir,
struct fat_dir *ent, unsigned long startcluster,
const struct fat_dir *parent_dir);
extern int fat_getnext(struct fat_dir *ent, struct fat_direntry *entry);
extern unsigned int fat_get_cluster_size(IF_MV_NONVOID(int volume)); /* public for debug info screen */