summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Sevakis <jethead71@rockbox.org>2017-01-18 08:29:54 -0500
committerMichael Sevakis <jethead71@rockbox.org>2017-03-21 23:11:55 -0400
commitbfd04df4807197261b9d36f16c5788a733129c05 (patch)
treed86208f0908516579fb7324fa880e8baef191d98
parent05739efe8d9df2a83b983a86d07cb0715c6d19ee (diff)
downloadrockbox-bfd04df4807197261b9d36f16c5788a733129c05.tar.gz
rockbox-bfd04df4807197261b9d36f16c5788a733129c05.zip
Dircache: Improve freed name memory recallocation
There's only a need to check every MAX_TINYNAME+1 bytes and that the last character of the needed size 0xff in order to verify the size of the block since the minimum indirectly-stored string is MAX_TINYNAME+1. Change-Id: Ic789376b8575bab9266fcd54c610db0961de5d7f
-rw-r--r--firmware/common/dircache.c62
1 files changed, 36 insertions, 26 deletions
diff --git a/firmware/common/dircache.c b/firmware/common/dircache.c
index e8f149cad0..0cdaf1bd4a 100644
--- a/firmware/common/dircache.c
+++ b/firmware/common/dircache.c
@@ -781,56 +781,66 @@ static int alloc_name(size_t size)
781 if (dircache.namesfree >= size) 781 if (dircache.namesfree >= size)
782 { 782 {
783 /* scan for a free gap starting at the hint point - first fit */ 783 /* scan for a free gap starting at the hint point - first fit */
784 unsigned char *start = get_name(dircache.nextnamefree), *p = start; 784 unsigned char * const start = get_name(dircache.nextnamefree);
785 unsigned char *namesend = get_name(dircache.names + dircache.sizenames); 785 unsigned char * const bufend = get_name(dircache.names + dircache.sizenames);
786 size_t gapsize = 0; 786 unsigned char *p = start;
787 unsigned char *end = bufend;
787 788
788 while (gapsize < size) 789 while (1)
789 { 790 {
790 if ((p = memchr(p, 0xff, namesend - p))) 791 if ((size_t)(bufend - p) >= size && (p = memchr(p, 0xff, end - p)))
791 { 792 {
792 /* found a sentinel; see if there are enough in a row */ 793 /* found a sentinel; see if there are enough in a row */
793 gapsize = 1; 794 unsigned char *q = p + size - 1;
794 while (*++p == 0xff && gapsize < size) 795
795 gapsize++; 796 /* check end byte and every MAX_TINYNAME+1 bytes from the end;
797 the minimum-length indirectly allocated string that could be
798 in between must have at least one character at one of those
799 locations */
800 while (q > p && *q == 0xff)
801 q -= MAX_TINYNAME+1;
802
803 if (q <= p)
804 {
805 nameidx = get_nameidx(p);
806 break;
807 }
808
809 p += size;
796 } 810 }
797 else 811 else
798 { 812 {
799 if (namesend == start) 813 if (end == start)
800 break; /* exhausted */ 814 break; /* exhausted */
801 815
802 /* wrap */ 816 /* wrap */
803 namesend = start; 817 end = start;
804 p = get_name(dircache.names); 818 p = get_name(dircache.names);
805 819
806 if (p == namesend) 820 if (p == end)
807 break; /* initial hint was at names start */ 821 break; /* initial hint was at names start */
808 } 822 }
809 } 823 }
810 824
811 if (gapsize >= size) 825 if (nameidx)
812 { 826 {
813 unsigned char *namep = p - gapsize; 827 unsigned char *q = p + size;
814 nameidx = get_nameidx(namep); 828 if (q[0] == 0xff && q[MAX_TINYNAME] != 0xff)
815
816 if (*p == 0xff)
817 { 829 {
818 /* if only a tiny block remains after buffer, claim it too */ 830 /* if only a tiny block remains after buffer, claim it and
819 size_t tinysize = 1; 831 hide it from scans since it's too small for indirect
820 while (*++p == 0xff && tinysize <= MAX_TINYNAME) 832 allocation */
821 tinysize++; 833 do
822
823 if (tinysize <= MAX_TINYNAME)
824 { 834 {
825 /* mark with tiny block sentinel */ 835 *q = 0xfe;
826 memset(p - tinysize, 0xfe, tinysize); 836 size++;
827 size += tinysize;
828 } 837 }
838 while (*++q == 0xff);
829 } 839 }
830 840
831 dircache.namesfree -= size; 841 dircache.namesfree -= size;
832 dircache.sizeused += size; 842 dircache.sizeused += size;
833 set_namesfree_hint(namep + size); 843 set_namesfree_hint(p + size);
834 } 844 }
835 } 845 }
836 846