summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Martitz <kugel@rockbox.org>2011-06-20 20:12:30 +0000
committerThomas Martitz <kugel@rockbox.org>2011-06-20 20:12:30 +0000
commit52abbb186d62f113eb468754332840d54bd7e756 (patch)
tree51ea81d81391e5b01c1d0cb684b61d22c6741a41
parentb67f4a1824602d18fbf28b7428bd12186f017f78 (diff)
downloadrockbox-52abbb186d62f113eb468754332840d54bd7e756.tar.gz
rockbox-52abbb186d62f113eb468754332840d54bd7e756.zip
Dircache: Change internal cache layout.
The dircache_entry structs are now allocated subsequently from the front, allowing to treat them as an array. The d_names are allocated from the back (in reverse order, growing downwards). This allows the cache to be moved around (needed for my buflib gsoc project). It is utilized when loading the cache from disk (on the h100), now the pointer to the cache begin doesn't need to be the same across reboots anymore. This should save a bit memory usage, since there's no need for aligning padding bytes after d_names anymore. git-svn-id: svn://svn.rockbox.org/rockbox/trunk@30036 a1c6a512-1295-4272-9138-f99709370657
-rw-r--r--firmware/common/dircache.c231
-rw-r--r--firmware/include/dircache.h3
2 files changed, 170 insertions, 64 deletions
diff --git a/firmware/common/dircache.c b/firmware/common/dircache.c
index 92eba42f65..56c408e02c 100644
--- a/firmware/common/dircache.c
+++ b/firmware/common/dircache.c
@@ -47,6 +47,7 @@
47#endif 47#endif
48#include "rbpaths.h" 48#include "rbpaths.h"
49 49
50
50/* Queue commands. */ 51/* Queue commands. */
51#define DIRCACHE_BUILD 1 52#define DIRCACHE_BUILD 1
52#define DIRCACHE_STOP 2 53#define DIRCACHE_STOP 2
@@ -57,9 +58,30 @@
57#define MAX_OPEN_DIRS 8 58#define MAX_OPEN_DIRS 8
58#endif 59#endif
59static DIR_CACHED opendirs[MAX_OPEN_DIRS]; 60static DIR_CACHED opendirs[MAX_OPEN_DIRS];
60 61/* Cache Layout:
62 *
63 * x - array of struct dircache_entry
64 * r - reserved buffer
65 * d - name buffer for the d_name entry of the struct dircache_entry
66 * |xxxxxx|rrrrrrrrr|dddddd|
67 *
68 * subsequent x are allocated from the front, d are allocated from the back,
69 * using the reserve buffer for entries added after initial scan
70 *
71 * after a while the cache may look like:
72 * |xxxxxxxx|rrrrr|dddddddd|
73 *
74 * after a reboot, the reserve buffer is restored in it's size, so that the
75 * total allocation size grows
76 * |xxxxxxxx|rrrrrrrrr|dddddddd|
77 */
61static struct dircache_entry *fd_bindings[MAX_OPEN_FILES]; 78static struct dircache_entry *fd_bindings[MAX_OPEN_FILES];
79/* this points to the beginnging of the buffer and the first entry */
62static struct dircache_entry *dircache_root; 80static struct dircache_entry *dircache_root;
81/* these point to the start and end of the name buffer (d above) */
82static char *d_names_start, *d_names_end;
83/* put "." and ".." into the d_names buffer to enable easy pointer logic */
84static char *dot, *dotdot;
63#ifdef HAVE_MULTIVOLUME 85#ifdef HAVE_MULTIVOLUME
64static struct dircache_entry *append_position; 86static struct dircache_entry *append_position;
65#endif 87#endif
@@ -116,20 +138,12 @@ static struct dircache_entry* allocate_entry(void)
116 return NULL; 138 return NULL;
117 } 139 }
118 140
119 next_entry = (struct dircache_entry *)((char *)dircache_root+dircache_size); 141 next_entry = &dircache_root[entry_count++];
120#ifdef ROCKBOX_STRICT_ALIGN
121 /* Make sure the entry is long aligned. */
122 if ((long)next_entry & 0x03)
123 {
124 dircache_size += 4 - ((long)next_entry & 0x03);
125 next_entry = (struct dircache_entry *)(((long)next_entry & ~0x03) + 0x04);
126 }
127#endif
128 next_entry->d_name = NULL; 142 next_entry->d_name = NULL;
129 next_entry->up = NULL; 143 next_entry->up = NULL;
130 next_entry->down = NULL; 144 next_entry->down = NULL;
131 next_entry->next = NULL; 145 next_entry->next = NULL;
132 146
133 dircache_size += sizeof(struct dircache_entry); 147 dircache_size += sizeof(struct dircache_entry);
134 148
135 return next_entry; 149 return next_entry;
@@ -230,8 +244,9 @@ static int sab_process_dir(unsigned long startcluster, struct dircache_entry *ce
230 if(!strcmp(".", sab.direntry->name) || 244 if(!strcmp(".", sab.direntry->name) ||
231 !strcmp("..", sab.direntry->name)) 245 !strcmp("..", sab.direntry->name))
232 continue; 246 continue;
233 247
234 ce->d_name = ((char *)dircache_root + dircache_size); 248 size_t size = strlen(sab.direntry->name) + 1;
249 ce->d_name = (d_names_start -= size);
235 ce->startcluster = sab.direntry->firstcluster; 250 ce->startcluster = sab.direntry->firstcluster;
236 ce->info.size = sab.direntry->filesize; 251 ce->info.size = sab.direntry->filesize;
237 ce->info.attribute = sab.direntry->attr; 252 ce->info.attribute = sab.direntry->attr;
@@ -239,9 +254,7 @@ static int sab_process_dir(unsigned long startcluster, struct dircache_entry *ce
239 ce->info.wrttime = sab.direntry->wrttime; 254 ce->info.wrttime = sab.direntry->wrttime;
240 255
241 strcpy(ce->d_name, sab.direntry->name); 256 strcpy(ce->d_name, sab.direntry->name);
242 dircache_size += strlen(ce->d_name) + 1; 257 dircache_size += size;
243
244 entry_count++;
245 258
246 if(ce->info.attribute & FAT_ATTR_DIRECTORY) 259 if(ce->info.attribute & FAT_ATTR_DIRECTORY)
247 dircache_gen_down(ce); 260 dircache_gen_down(ce);
@@ -261,7 +274,7 @@ static int sab_process_dir(unsigned long startcluster, struct dircache_entry *ce
261 } 274 }
262 275
263 /* add "." and ".." */ 276 /* add "." and ".." */
264 ce->d_name = "."; 277 ce->d_name = dot;
265 ce->info.attribute = FAT_ATTR_DIRECTORY; 278 ce->info.attribute = FAT_ATTR_DIRECTORY;
266 ce->startcluster = startcluster; 279 ce->startcluster = startcluster;
267 ce->info.size = 0; 280 ce->info.size = 0;
@@ -269,7 +282,7 @@ static int sab_process_dir(unsigned long startcluster, struct dircache_entry *ce
269 282
270 ce = dircache_gen_next(ce); 283 ce = dircache_gen_next(ce);
271 284
272 ce->d_name = ".."; 285 ce->d_name = dotdot;
273 ce->info.attribute = FAT_ATTR_DIRECTORY; 286 ce->info.attribute = FAT_ATTR_DIRECTORY;
274 ce->startcluster = (first_ce->up ? first_ce->up->startcluster : 0); 287 ce->startcluster = (first_ce->up ? first_ce->up->startcluster : 0);
275 ce->info.size = 0; 288 ce->info.size = 0;
@@ -300,7 +313,7 @@ static int dircache_scan_and_build(IF_MV2(int volume,) struct dircache_entry *ce
300#ifdef HAVE_MULTIVOLUME 313#ifdef HAVE_MULTIVOLUME
301 if (volume > 0) 314 if (volume > 0)
302 { 315 {
303 ce->d_name = ((char *)dircache_root+dircache_size); 316 ce->d_name = (d_names_start -= sizeof(VOL_NAMES));
304 size_t len = snprintf(ce->d_name, VOL_ENUM_POS + 3, VOL_NAMES, volume)+1; 317 size_t len = snprintf(ce->d_name, VOL_ENUM_POS + 3, VOL_NAMES, volume)+1;
305 dircache_size += len; 318 dircache_size += len;
306 ce->info.attribute = FAT_ATTR_DIRECTORY | FAT_ATTR_VOLUME; 319 ce->info.attribute = FAT_ATTR_DIRECTORY | FAT_ATTR_VOLUME;
@@ -338,14 +351,13 @@ static int sab_process_dir(struct dircache_entry *ce)
338 if(!strcmp(".", entry->d_name) || 351 if(!strcmp(".", entry->d_name) ||
339 !strcmp("..", entry->d_name)) 352 !strcmp("..", entry->d_name))
340 continue; 353 continue;
341 354
342 ce->d_name = ((char *)dircache_root + dircache_size); 355 size_t size = strlen(entry->d_name) + 1;
356 ce->d_name = (d_names_start -= size);
343 ce->info = entry->info; 357 ce->info = entry->info;
344 358
345 strcpy(ce->d_name, entry->d_name); 359 strcpy(ce->d_name, entry->d_name);
346 dircache_size += strlen(entry->d_name) + 1; 360 dircache_size += size;
347
348 entry_count++;
349 361
350 if(entry->info.attribute & ATTR_DIRECTORY) 362 if(entry->info.attribute & ATTR_DIRECTORY)
351 { 363 {
@@ -387,14 +399,14 @@ static int sab_process_dir(struct dircache_entry *ce)
387 } 399 }
388 400
389 /* add "." and ".." */ 401 /* add "." and ".." */
390 ce->d_name = "."; 402 ce->d_name = dot;
391 ce->info.attribute = ATTR_DIRECTORY; 403 ce->info.attribute = ATTR_DIRECTORY;
392 ce->info.size = 0; 404 ce->info.size = 0;
393 ce->down = first_ce; 405 ce->down = first_ce;
394 406
395 ce = dircache_gen_next(ce); 407 ce = dircache_gen_next(ce);
396 408
397 ce->d_name = ".."; 409 ce->d_name = dotdot;
398 ce->info.attribute = ATTR_DIRECTORY; 410 ce->info.attribute = ATTR_DIRECTORY;
399 ce->info.size = 0; 411 ce->info.size = 0;
400 ce->down = first_ce->up; 412 ce->down = first_ce->up;
@@ -498,7 +510,7 @@ static struct dircache_entry* dircache_get_entry(const char *path, bool go_down)
498int dircache_load(void) 510int dircache_load(void)
499{ 511{
500 struct dircache_maindata maindata; 512 struct dircache_maindata maindata;
501 int bytes_read; 513 ssize_t bytes_read;
502 int fd; 514 int fd;
503 515
504 if (dircache_initialized) 516 if (dircache_initialized)
@@ -520,29 +532,65 @@ int dircache_load(void)
520 remove_dircache_file(); 532 remove_dircache_file();
521 return -3; 533 return -3;
522 } 534 }
523
524 dircache_root = buffer_alloc(0);
525 if ((long)maindata.root_entry != (long)dircache_root)
526 {
527 logf("Position missmatch");
528 close(fd);
529 remove_dircache_file();
530 return -4;
531 }
532 535
533 dircache_root = buffer_alloc(maindata.size + DIRCACHE_RESERVE); 536 dircache_root = buffer_alloc(maindata.size + DIRCACHE_RESERVE);
534 entry_count = maindata.entry_count; 537 entry_count = maindata.entry_count;
535 appflags = maindata.appflags; 538 appflags = maindata.appflags;
536 bytes_read = read(fd, dircache_root, MIN(DIRCACHE_LIMIT, maindata.size)); 539
537 close(fd); 540 /* read the dircache file into memory,
538 remove_dircache_file(); 541 * start with the struct dircache_entries */
542 ssize_t bytes_to_read = entry_count*sizeof(struct dircache_entry);
543 bytes_read = read(fd, dircache_root, bytes_to_read);
539 544
540 if (bytes_read != maindata.size) 545 if (bytes_read != bytes_to_read)
541 { 546 {
542 logf("Dircache read failed"); 547 logf("Dircache read failed #1");
543 return -6; 548 return -6;
544 } 549 }
545 550
551 /* continue with the d_names. Fix up pointers to them if needed */
552 d_names_start = ((char*)&dircache_root[entry_count] + DIRCACHE_RESERVE);
553 bytes_to_read = maindata.size - bytes_to_read;
554 bytes_read = read(fd, d_names_start, bytes_to_read);
555 close(fd);
556 remove_dircache_file();
557 if (bytes_read != bytes_to_read)
558 {
559 logf("Dircache read failed #2");
560 return -7;
561 }
562
563 d_names_end = &d_names_start[bytes_read];
564 dot = d_names_end - sizeof(".");
565 dotdot = dot - sizeof("..");
566
567 /* d_names are in reverse order, so the last entry points to the first string */
568 intptr_t offset_d_names = maindata.d_names_start - d_names_start;
569 intptr_t offset_entries = maindata.root_entry - dircache_root;
570
571 /* offset_entries is less likely to differ, so check if it's 0 in the loop
572 * offset_d_names however is almost always non-zero, since dircache_save()
573 * creates a file which causes the reserve buffer to be used. since
574 * we allocate a new, empty DIRCACHE_RESERVE here, the strings are
575 * farther behind */
576 if (offset_entries != 0 || offset_d_names != 0)
577 {
578 for(unsigned i = 0; i < entry_count; i++)
579 {
580 if (dircache_root[i].d_name)
581 dircache_root[i].d_name -= offset_d_names;
582
583 if (offset_entries == 0)
584 continue;
585 if (dircache_root[i].next)
586 dircache_root[i].next -= offset_entries;
587 if (dircache_root[i].up)
588 dircache_root[i].up -= offset_entries;
589 if (dircache_root[i].down)
590 dircache_root[i].down -= offset_entries;
591 }
592 }
593
546 /* Cache successfully loaded. */ 594 /* Cache successfully loaded. */
547 dircache_size = maindata.size; 595 dircache_size = maindata.size;
548 allocated_size = dircache_size + DIRCACHE_RESERVE; 596 allocated_size = dircache_size + DIRCACHE_RESERVE;
@@ -575,6 +623,7 @@ int dircache_save(void)
575 maindata.magic = DIRCACHE_MAGIC; 623 maindata.magic = DIRCACHE_MAGIC;
576 maindata.size = dircache_size; 624 maindata.size = dircache_size;
577 maindata.root_entry = dircache_root; 625 maindata.root_entry = dircache_root;
626 maindata.d_names_start = d_names_start;
578 maindata.entry_count = entry_count; 627 maindata.entry_count = entry_count;
579 maindata.appflags = appflags; 628 maindata.appflags = appflags;
580 629
@@ -587,14 +636,26 @@ int dircache_save(void)
587 return -2; 636 return -2;
588 } 637 }
589 638
590 /* Dump whole directory cache to disk */ 639 /* Dump whole directory cache to disk
591 bytes_written = write(fd, dircache_root, dircache_size); 640 * start by writing the dircache_entries */
592 close(fd); 641 size_t bytes_to_write = entry_count*sizeof(struct dircache_entry);
593 if (bytes_written != dircache_size) 642 bytes_written = write(fd, dircache_root, bytes_to_write);
643 if (bytes_written != bytes_to_write)
594 { 644 {
595 logf("dircache: write failed #2"); 645 logf("dircache: write failed #2");
596 return -3; 646 return -3;
597 } 647 }
648
649 /* continue with the d_names */
650 bytes_to_write = d_names_end - d_names_start;
651 bytes_written = write(fd, d_names_start, bytes_to_write);
652 close(fd);
653 if (bytes_written != bytes_to_write)
654 {
655 logf("dircache: write failed #3");
656 return -4;
657 }
658
598 659
599 return 0; 660 return 0;
600} 661}
@@ -605,6 +666,7 @@ int dircache_save(void)
605 */ 666 */
606static int dircache_do_rebuild(void) 667static int dircache_do_rebuild(void)
607{ 668{
669 struct dircache_entry* root_entry;
608 unsigned int start_tick; 670 unsigned int start_tick;
609 int i; 671 int i;
610 672
@@ -612,12 +674,13 @@ static int dircache_do_rebuild(void)
612 start_tick = current_tick; 674 start_tick = current_tick;
613 dircache_initializing = true; 675 dircache_initializing = true;
614 appflags = 0; 676 appflags = 0;
677
678 /* reset dircache and alloc root entry */
615 entry_count = 0; 679 entry_count = 0;
616 680 root_entry = allocate_entry();
617 dircache_size = sizeof(struct dircache_entry);
618 681
619#ifdef HAVE_MULTIVOLUME 682#ifdef HAVE_MULTIVOLUME
620 append_position = dircache_root; 683 append_position = root_entry;
621 684
622 for (i = NUM_VOLUMES; i >= 0; i--) 685 for (i = NUM_VOLUMES; i >= 0; i--)
623 { 686 {
@@ -628,7 +691,7 @@ static int dircache_do_rebuild(void)
628#ifdef HAVE_MULTIVOLUME 691#ifdef HAVE_MULTIVOLUME
629 if (dircache_scan_and_build(IF_MV2(i,) append_position) < 0) 692 if (dircache_scan_and_build(IF_MV2(i,) append_position) < 0)
630#else 693#else
631 if (dircache_scan_and_build(IF_MV2(0,) dircache_root) < 0) 694 if (dircache_scan_and_build(IF_MV2(0,) root_entry) < 0)
632#endif /* HAVE_MULTIVOLUME */ 695#endif /* HAVE_MULTIVOLUME */
633 { 696 {
634 logf("dircache_scan_and_build failed"); 697 logf("dircache_scan_and_build failed");
@@ -660,14 +723,6 @@ static int dircache_do_rebuild(void)
660 if (allocated_size - dircache_size < DIRCACHE_RESERVE) 723 if (allocated_size - dircache_size < DIRCACHE_RESERVE)
661 reserve_used = DIRCACHE_RESERVE - (allocated_size - dircache_size); 724 reserve_used = DIRCACHE_RESERVE - (allocated_size - dircache_size);
662 } 725 }
663 else
664 {
665 /* We have to long align the audiobuf to keep the buffer access fast. */
666 audiobuf += (long)((dircache_size & ~0x03) + 0x04);
667 audiobuf += DIRCACHE_RESERVE;
668 allocated_size = dircache_size + DIRCACHE_RESERVE;
669 reserve_used = 0;
670 }
671 726
672 return 1; 727 return 1;
673} 728}
@@ -712,6 +767,14 @@ static void dircache_thread(void)
712 } 767 }
713} 768}
714 769
770static void generate_dot_d_names(void)
771{
772 dot = (d_names_start -= sizeof("."));
773 dotdot = (d_names_start -= sizeof(".."));
774 dircache_size += sizeof(".") + sizeof("..");
775 strcpy(dot, ".");
776 strcpy(dotdot, "..");
777}
715/** 778/**
716 * Start scanning the disk to build the dircache. 779 * Start scanning the disk to build the dircache.
717 * Either transparent or non-transparent build method is used. 780 * Either transparent or non-transparent build method is used.
@@ -734,22 +797,63 @@ int dircache_build(int last_size)
734 queue_post(&dircache_queue, DIRCACHE_BUILD, 0); 797 queue_post(&dircache_queue, DIRCACHE_BUILD, 0);
735 return 2; 798 return 2;
736 } 799 }
737
738 if (last_size > DIRCACHE_RESERVE && last_size < DIRCACHE_LIMIT ) 800 if (last_size > DIRCACHE_RESERVE && last_size < DIRCACHE_LIMIT )
739 { 801 {
740 allocated_size = last_size + DIRCACHE_RESERVE; 802 allocated_size = last_size + DIRCACHE_RESERVE;
741 dircache_root = buffer_alloc(allocated_size); 803 dircache_root = buffer_alloc(allocated_size);
804 d_names_start =
805 d_names_end = ((char*)dircache_root)+allocated_size-1;
806 dircache_size = 0;
742 thread_enabled = true; 807 thread_enabled = true;
808 generate_dot_d_names();
743 809
744 /* Start a transparent rebuild. */ 810 /* Start a transparent rebuild. */
745 queue_post(&dircache_queue, DIRCACHE_BUILD, 0); 811 queue_post(&dircache_queue, DIRCACHE_BUILD, 0);
746 return 3; 812 return 3;
747 } 813 }
748 814
749 dircache_root = (struct dircache_entry *)(((long)audiobuf & ~0x03) + 0x04); 815 /* struct dircache_entrys are allocated from the beginning,
816 * their corresponding d_name from the end
817 * after generation the buffer will be compacted with DIRCACHE_RESERVE
818 * free bytes in between */
819 audiobuf = (char*)(((intptr_t)audiobuf & ~0x03) + 0x04);
820 dircache_root = (struct dircache_entry*)audiobuf;
821 d_names_start = d_names_end = audiobufend - 1;
822 dircache_size = 0;
823 generate_dot_d_names();
750 824
751 /* Start a non-transparent rebuild. */ 825 /* Start a non-transparent rebuild. */
752 return dircache_do_rebuild(); 826 int res = dircache_do_rebuild();
827
828 /** compact the dircache buffer **/
829 if (res >= 0)
830 {
831 char* dst = ((char*)&dircache_root[entry_count] + DIRCACHE_RESERVE);
832 ssize_t offset = d_names_start - dst;
833 if (offset > 0)
834 {
835 ssize_t size_to_move = dircache_size -
836 entry_count*sizeof(struct dircache_entry);
837 /* move d_names down, use memmove if overlap */
838 if (offset > size_to_move)
839 memcpy(dst, d_names_start, size_to_move);
840 else
841 memmove(dst, d_names_start, size_to_move);
842
843 /* fix up pointers to the d_names */
844 for(unsigned i = 0; i < entry_count; i++)
845 dircache_root[i].d_name -= offset;
846
847 d_names_end -= offset;
848 /* equivalent to dircache_size + DIRCACHE_RESERVE */
849 allocated_size = (d_names_end - (char*)dircache_root);
850 reserve_used = 0;
851 audiobuf += allocated_size;
852 }
853 else /* something went wrong */
854 return -1;
855 }
856 return res;
753} 857}
754 858
755/** 859/**
@@ -1008,14 +1112,15 @@ static struct dircache_entry* dircache_new_entry(const char *path, int attribute
1008 return NULL; 1112 return NULL;
1009 } 1113 }
1010 } 1114 }
1011 1115
1012 entry->d_name = ((char *)dircache_root+dircache_size); 1116 size_t size = strlen(new) + 1;
1117 entry->d_name = (d_names_start -= size);
1013 entry->startcluster = 0; 1118 entry->startcluster = 0;
1014 memset(&entry->info, 0, sizeof(entry->info)); 1119 memset(&entry->info, 0, sizeof(entry->info));
1015 entry->info.attribute = attribute; 1120 entry->info.attribute = attribute;
1016 1121
1017 strcpy(entry->d_name, new); 1122 strcpy(entry->d_name, new);
1018 dircache_size += strlen(entry->d_name) + 1; 1123 dircache_size += size;
1019 1124
1020 if (attribute & ATTR_DIRECTORY) 1125 if (attribute & ATTR_DIRECTORY)
1021 { 1126 {
diff --git a/firmware/include/dircache.h b/firmware/include/dircache.h
index 716d1fbed2..d04c176996 100644
--- a/firmware/include/dircache.h
+++ b/firmware/include/dircache.h
@@ -47,13 +47,14 @@ struct travel_data {
47 int pathpos; 47 int pathpos;
48}; 48};
49 49
50#define DIRCACHE_MAGIC 0x00d0c0a0 50#define DIRCACHE_MAGIC 0x00d0c0a1
51struct dircache_maindata { 51struct dircache_maindata {
52 long magic; 52 long magic;
53 long size; 53 long size;
54 long entry_count; 54 long entry_count;
55 long appflags; 55 long appflags;
56 struct dircache_entry *root_entry; 56 struct dircache_entry *root_entry;
57 char *d_names_start;
57}; 58};
58 59
59#define MAX_PENDING_BINDINGS 2 60#define MAX_PENDING_BINDINGS 2