summaryrefslogtreecommitdiff
path: root/src/drivers/ao_fat.c
diff options
context:
space:
mode:
authorKeith Packard <keithp@keithp.com>2013-03-28 16:57:02 -0700
committerKeith Packard <keithp@keithp.com>2013-03-28 16:57:02 -0700
commit8101e4af199a3d79bff434f788cce9f97aeac53a (patch)
tree188b593e82e3319be67966163bc0c344f24e96e9 /src/drivers/ao_fat.c
parentc7b606e93a4e4fbd2c0e883352ed74619ee24cf7 (diff)
altos: Add a simple cache for the FAT position->cluster computation
This improves read/write performance with large files by not re-walking the cluster chain for every operation Signed-off-by: Keith Packard <keithp@keithp.com>
Diffstat (limited to 'src/drivers/ao_fat.c')
-rw-r--r--src/drivers/ao_fat.c101
1 files changed, 58 insertions, 43 deletions
diff --git a/src/drivers/ao_fat.c b/src/drivers/ao_fat.c
index c0412380..65c5ea7c 100644
--- a/src/drivers/ao_fat.c
+++ b/src/drivers/ao_fat.c
@@ -136,7 +136,6 @@ ao_fat_entry_read(uint16_t cluster)
if (!ao_fat_cluster_valid(cluster))
return 0xfff7;
-// cluster -= 2;
sector = cluster >> (SECTOR_SHIFT - 1);
offset = (cluster << 1) & SECTOR_MASK;
buf = ao_fat_sector_get(fat_start + sector);
@@ -162,7 +161,6 @@ ao_fat_entry_replace(uint16_t cluster, uint16_t new_value)
if (!ao_fat_cluster_valid(cluster))
return 0;
-// cluster -= 2;
sector = cluster >> (SECTOR_SHIFT - 1);
offset = (cluster << 1) & SECTOR_MASK;
buf = ao_fat_sector_get(fat_start + sector);
@@ -203,8 +201,11 @@ ao_fat_free_cluster_chain(uint16_t cluster)
/*
* ao_fat_cluster_seek
*
- * Walk a cluster chain the specified distance and return what we find
- * there. If distance is zero, return the provided cluster.
+ * The basic file system operation -- map a file cluster index to a
+ * partition cluster number. Done by computing the cluster number and
+ * then walking that many clusters from the first cluster. Returns
+ * 0xffff if we walk off the end of the file or the cluster chain
+ * is damaged somehow
*/
static uint16_t
ao_fat_cluster_seek(uint16_t cluster, uint16_t distance)
@@ -219,34 +220,6 @@ ao_fat_cluster_seek(uint16_t cluster, uint16_t distance)
}
/*
- * ao_fat_sector_seek
- *
- * The basic file system operation -- map a file sector number to a
- * partition sector number. Done by computing the cluster number and
- * then walking that many clusters from the first cluster, then
- * adding the sector offset from the start of the cluster. Returns
- * 0xffffffff if we walk off the end of the file or the cluster chain
- * is damaged somehow
- */
-static uint32_t
-ao_fat_sector_seek(uint16_t cluster, uint32_t sector)
-{
- uint16_t distance;
- uint16_t offset;
-
- distance = sector / sectors_per_cluster;
- offset = sector % sectors_per_cluster;
-
- cluster = ao_fat_cluster_seek(cluster, distance);
-
- if (!ao_fat_cluster_valid(cluster))
- return 0xffffffff;
-
- /* Compute the sector within the partition and return it */
- return data_start + (uint32_t) (cluster-2) * sectors_per_cluster + offset;
-}
-
-/*
* ao_fat_setup_partition
*
* Load the boot block and find the first partition
@@ -385,14 +358,51 @@ ao_fat_setup(void)
static struct ao_fat_dirent ao_file_dirent;
static uint32_t ao_file_offset;
+static uint32_t ao_file_cluster_offset;
+static uint16_t ao_file_cluster;
static uint8_t ao_file_opened;
static uint32_t
-ao_fat_offset_to_sector(uint32_t offset)
+ao_fat_current_sector(void)
{
- if (offset > ao_file_dirent.size)
+ uint16_t cluster_offset;
+ uint32_t sector_offset;
+ uint16_t sector_index;
+ uint16_t cluster;
+
+ if (ao_file_offset > ao_file_dirent.size)
return 0xffffffff;
- return ao_fat_sector_seek(ao_file_dirent.cluster, offset >> SECTOR_SHIFT);
+
+ sector_offset = ao_file_offset >> SECTOR_SHIFT;
+
+ if (!ao_file_cluster) {
+ cluster_offset = sector_offset / sectors_per_cluster;
+
+ cluster = ao_fat_cluster_seek(ao_file_dirent.cluster, cluster_offset);
+ if (!ao_fat_cluster_valid(cluster))
+ return 0xffffffff;
+ ao_file_cluster = cluster;
+ ao_file_cluster_offset = cluster_offset * bytes_per_cluster;
+ }
+
+ sector_index = sector_offset % sectors_per_cluster;
+ return data_start + (uint32_t) (ao_file_cluster-2) * sectors_per_cluster + sector_index;
+}
+
+static void
+ao_fat_set_offset(uint32_t offset)
+{
+
+ if (offset == 0) {
+ ao_file_cluster = ao_file_dirent.cluster;
+ ao_file_cluster_offset = 0;
+ }
+ else if (offset < ao_file_cluster_offset ||
+ ao_file_cluster_offset + bytes_per_cluster <= offset)
+ {
+ ao_file_cluster = 0;
+ }
+ ao_file_offset = offset;
}
/*
@@ -576,7 +586,7 @@ ao_fat_open(char name[11], uint8_t mode)
if (mode > AO_FAT_OPEN_READ && (dirent.attr & AO_FAT_FILE_READ_ONLY))
return -AO_FAT_EACCESS;
ao_file_dirent = dirent;
- ao_file_offset = 0;
+ ao_fat_set_offset(0);
ao_file_opened = 1;
return AO_FAT_SUCCESS;
}
@@ -620,6 +630,7 @@ ao_fat_creat(char name[11])
ao_fat_dirent_init(dent, entry, &ao_file_dirent);
ao_fat_root_put(dent, entry, 1);
ao_file_opened = 1;
+ ao_fat_set_offset(0);
status = -AO_FAT_SUCCESS;
break;
} else {
@@ -645,6 +656,7 @@ ao_fat_close(void)
memset(&ao_file_dirent, '\0', sizeof (struct ao_fat_dirent));
ao_file_offset = 0;
+ ao_file_cluster = 0;
ao_file_opened = 0;
ao_bufio_flush();
return AO_FAT_SUCCESS;
@@ -681,7 +693,7 @@ ao_fat_read(void *dst, int len)
else
this_time = SECTOR_SIZE - offset;
- sector = ao_fat_offset_to_sector(ao_file_offset);
+ sector = ao_fat_current_sector();
if (sector == 0xffffffff)
break;
buf = ao_fat_sector_get(sector);
@@ -695,7 +707,7 @@ ao_fat_read(void *dst, int len)
ret += this_time;
len -= this_time;
dst_b += this_time;
- ao_file_offset += this_time;
+ ao_fat_set_offset(ao_file_offset + this_time);
}
return ret;
}
@@ -731,7 +743,7 @@ ao_fat_write(void *src, int len)
else
this_time = SECTOR_SIZE - offset;
- sector = ao_fat_offset_to_sector(ao_file_offset);
+ sector = ao_fat_current_sector();
if (sector == 0xffffffff)
break;
buf = ao_fat_sector_get(sector);
@@ -745,7 +757,7 @@ ao_fat_write(void *src, int len)
ret += this_time;
len -= this_time;
src_b += this_time;
- ao_file_offset += this_time;
+ ao_fat_set_offset(ao_file_offset + this_time);
}
return ret;
}
@@ -762,20 +774,23 @@ ao_fat_write(void *src, int len)
int32_t
ao_fat_seek(int32_t pos, uint8_t whence)
{
+ uint32_t new_offset = ao_file_offset;
+
if (!ao_file_opened)
return -AO_FAT_EBADF;
switch (whence) {
case AO_FAT_SEEK_SET:
- ao_file_offset = pos;
+ new_offset = pos;
break;
case AO_FAT_SEEK_CUR:
- ao_file_offset += pos;
+ new_offset += pos;
break;
case AO_FAT_SEEK_END:
- ao_file_offset = ao_file_dirent.size + pos;
+ new_offset = ao_file_dirent.size + pos;
break;
}
+ ao_fat_set_offset(new_offset);
return ao_file_offset;
}