stream-like access added

This commit is contained in:
Wiesner András 2023-10-03 15:37:39 +02:00
parent de52ac5d35
commit 8b8dcbe170
2 changed files with 184 additions and 11 deletions

View File

@ -236,11 +236,12 @@ static inline uint32_t fat32_get_first_cluster(const Fat32_FileEntry *entry) {
* The function locates a file based on its full path. * The function locates a file based on its full path.
* @param ctrl pointer to a FAT32 control block * @param ctrl pointer to a FAT32 control block
* @param path full path of the file to be located * @param path full path of the file to be located
* @param file_entry_ba if not NULL, then the function returns here the file entry's byte address (on the mass storage); untouched if file not found
* @return pointer to the file entry. Caution! This pointer is only valid until no further * @return pointer to the file entry. Caution! This pointer is only valid until no further
* call is made to fat32_* functions, since all functions involve the same buffer. * call is made to fat32_* functions, since all functions involve the same buffer.
* Functions invalidating buffer content are marked with (I). * Functions invalidating buffer content are marked with (I).
*/ */
const Fat32_FileEntry *fat32_locate_file(const Fat32_CtrlBlock *ctrl, const char *path) { const Fat32_FileEntry *fat32_locate_file(const Fat32_CtrlBlock *ctrl, const char *path, uint32_t *file_entry_ba) {
FETCH_MSTG; FETCH_MSTG;
// start search in the root directory // start search in the root directory
@ -256,7 +257,7 @@ const Fat32_FileEntry *fat32_locate_file(const Fat32_CtrlBlock *ctrl, const char
char entry_name[FAT32_MAX_BASENAME_LENGTH + 1]; // entry name char entry_name[FAT32_MAX_BASENAME_LENGTH + 1]; // entry name
Fat32_FileEntry *entry = NULL; // entry iterator Fat32_FileEntry *entry = NULL; // entry iterator
while ((!file_found) && (!no_such_file)) { while ((!file_found) && (!no_such_file)) { // traversing tree levels
// extract first basename // extract first basename
uint16_t basename_len = fat32_extract_next_basename_length(path_iter); // get basename length uint16_t basename_len = fat32_extract_next_basename_length(path_iter); // get basename length
uint16_t copy_len = MIN(basename_len, FAT32_MAX_BASENAME_LENGTH); uint16_t copy_len = MIN(basename_len, FAT32_MAX_BASENAME_LENGTH);
@ -271,7 +272,7 @@ const Fat32_FileEntry *fat32_locate_file(const Fat32_CtrlBlock *ctrl, const char
// acquire pointer to the beginning of the file entry table // acquire pointer to the beginning of the file entry table
entry = (Fat32_FileEntry *) buffer; entry = (Fat32_FileEntry *) buffer;
bool basename_entry_found = false; bool basename_entry_found = false;
while ((entry->short_fname[0] != 0x00) && (!basename_entry_found)) { while ((entry->short_fname[0] != 0x00) && (!basename_entry_found)) { // traversing siblings
if (entry->short_fname[0] != FAT32_UNUSED_FILE_ENTRY) { if (entry->short_fname[0] != FAT32_UNUSED_FILE_ENTRY) {
fat32_get_file_entry(&entry, entry_name); fat32_get_file_entry(&entry, entry_name);
@ -304,6 +305,9 @@ const Fat32_FileEntry *fat32_locate_file(const Fat32_CtrlBlock *ctrl, const char
if (basename_entry_found) { if (basename_entry_found) {
if (path_iter[0] == '\0') { // ...and it's the end of the path if (path_iter[0] == '\0') { // ...and it's the end of the path
file_found = true; // ...then the file is found file_found = true; // ...then the file is found
if (file_entry_ba != NULL) { // if file entry writeback address if provided
*file_entry_ba = sector * ctrl->bytes_per_sector + (((uint8_t *) entry) - buffer);
}
} else { // ...if it's not the end of the path, then load the cluster corresponding to the entry } else { // ...if it's not the end of the path, then load the cluster corresponding to the entry
// determine sector number // determine sector number
uint32_t next_cluster = fat32_get_first_cluster(entry); uint32_t next_cluster = fat32_get_first_cluster(entry);
@ -312,6 +316,8 @@ const Fat32_FileEntry *fat32_locate_file(const Fat32_CtrlBlock *ctrl, const char
// load sectors // load sectors
READ_MSTG_TO_FIRST_BUFFER(next_sector); READ_MSTG_TO_FIRST_BUFFER(next_sector);
READ_MSTG_TO_SECOND_BUFFER(next_sector + 1); READ_MSTG_TO_SECOND_BUFFER(next_sector + 1);
sector = next_sector; // advance sector
} }
} else { // if not found... } else { // if not found...
no_such_file = true; // ...then the file could not be located no_such_file = true; // ...then the file could not be located
@ -362,6 +368,30 @@ int fat32_list_dir(const Fat32_CtrlBlock *ctrl, const char *dir) {
return 0; return 0;
} }
/**
* Get the sequence number of the sector holding the byte pointed by address.
* @param ctrl pointer to FAT32 control block
* @param address byte address
* @return sequence number of the requested sector
*/
static inline uint32_t fat32_get_sector_by_byte_address(const Fat32_CtrlBlock *ctrl, uint32_t ba) {
return ba / ctrl->bytes_per_sector;
}
/**
* Get file entry based on its byte address. (I)
* @param ctrl pointer to FAT32 control block
* @param ba byte address of the file entry
* @return pointer to file entry
*/
static inline const Fat32_FileEntry * fat32_get_file_entry_by_byte_address(const Fat32_CtrlBlock * ctrl, uint32_t ba) {
uint32_t sector = fat32_get_sector_by_byte_address(ctrl, ba);
uint32_t offset = ba % ctrl->bytes_per_sector;
FETCH_MSTG;
READ_MSTG_TO_FIRST_BUFFER(sector);
return (Fat32_FileEntry *)(buffer + offset);
}
/** /**
* Get the sector's sequence number corresponding to the specific cluster identified by its sequence number. * Get the sector's sequence number corresponding to the specific cluster identified by its sequence number.
* @param ctrl pointer to FAT32 control block * @param ctrl pointer to FAT32 control block
@ -373,7 +403,7 @@ static inline uint32_t fat32_get_fat_entry_sector_by_cluster_index(const Fat32_C
} }
/** /**
* Gets the sequence number of the next chained cluster. (I) * Get the sequence number of the next chained cluster. (I)
* @param ctrl pointer to FAT32 control block * @param ctrl pointer to FAT32 control block
* @param cluster sequence number of a cluster * @param cluster sequence number of a cluster
* @return sequence number of the cluster following the passed one in the chain * @return sequence number of the cluster following the passed one in the chain
@ -388,7 +418,7 @@ static inline uint32_t fat32_get_next_chained_cluster(const Fat32_CtrlBlock *ctr
} }
/** /**
* Get sequence number of the cluster storing a specific byte position (pos) if the file's first cluster is given. * Get sequence number of the cluster storing a specific byte position (pos) if the file's first cluster is given. (I)
* @param ctrl pointer to FAT32 control block * @param ctrl pointer to FAT32 control block
* @param first_cluster sequence number of the first cluster of the file * @param first_cluster sequence number of the first cluster of the file
* @param pos byte position * @param pos byte position
@ -426,7 +456,8 @@ static inline uint32_t fat32_get_bytes_per_cluster(const Fat32_CtrlBlock *ctrl)
} }
/** /**
* Read a file using "random access". * Read a file using "random access". (I)
* This function holds a load of redundant code, but this is with intent.
* @param ctrl pointer to FAT32 control block * @param ctrl pointer to FAT32 control block
* @param entry pointer to the corresponding file entry * @param entry pointer to the corresponding file entry
* @param pos read start position * @param pos read start position
@ -434,7 +465,7 @@ static inline uint32_t fat32_get_bytes_per_cluster(const Fat32_CtrlBlock *ctrl)
* @param p pointer to output buffer * @param p pointer to output buffer
* @return the number of bytes read * @return the number of bytes read
*/ */
uint32_t fat32_read_file(const Fat32_CtrlBlock *ctrl, const Fat32_FileEntry *entry, uint32_t pos, uint32_t len, uint8_t *p) { uint32_t fat32_read_file_random_access(const Fat32_CtrlBlock *ctrl, const Fat32_FileEntry *entry, uint32_t pos, uint32_t len, uint8_t *p) {
// check position validity and check that the entry corresponds to a file, not to a directory or volume // check position validity and check that the entry corresponds to a file, not to a directory or volume
uint8_t attr = entry->attributes; uint8_t attr = entry->attributes;
if ((pos >= entry->size) || (attr & FAT32_FATT_DIRECTORY) || (attr & FAT32_FATT_VOLUME)) { if ((pos >= entry->size) || (attr & FAT32_FATT_DIRECTORY) || (attr & FAT32_FATT_VOLUME)) {
@ -490,3 +521,137 @@ uint32_t fat32_read_file(const Fat32_CtrlBlock *ctrl, const Fat32_FileEntry *ent
return read_len; return read_len;
} }
// -----------------
int fat32_open_file(const Fat32_CtrlBlock *ctrl, const char *path, Fat32_FileHelper *file_helper) {
uint32_t file_entry_ba;
const Fat32_FileEntry *entry = fat32_locate_file(ctrl, path, &file_entry_ba);
uint32_t first_cluster = fat32_get_first_cluster(entry);
// store data usable for identifying file on the storage
file_helper->ctrl = ctrl;
file_helper->entry_ba = file_entry_ba;
// store file information
file_helper->size = entry->size;
// seek to zero
file_helper->last_pos.pos = 0;
file_helper->last_pos.sector_begin_pos = 0;
file_helper->last_pos.cluster = first_cluster;
file_helper->last_pos.sector = fat32_get_first_sector_of_cluster(ctrl, first_cluster);
file_helper->last_pos.sector_of_cluster = 0;
return 0;
}
/**
* Seek file stream, set reader to pos.
* @param ctrl pointer to FAT32 control block
* @param pos stream position
* @param file_helper pointer to file helper, which gets updated
* @return 0 on success
*/
uint32_t fat32_seek_stream(const Fat32_CtrlBlock *ctrl, uint32_t pos, Fat32_FileHelper *file_helper) {
// check that we are not attempting to seek out of the file
if (pos >= file_helper->size) {
return -1;
}
// actual seeking
Fat32_SeekHint *seek_hint = &file_helper->last_pos; // fetch seek hint
if (pos > file_helper->last_pos.pos) { // if seeking forward, then seeking can be accelerated using the seek hints
uint32_t delta = pos - file_helper->last_pos.pos;
uint32_t bytes_to_sector_end = ctrl->bytes_per_sector - (file_helper->last_pos.pos % ctrl->bytes_per_sector); // determine byte count to sector end
if (delta < bytes_to_sector_end) { // if seeking does not cross sector boundary
seek_hint->pos = pos; // just change position
} else { // ...if crosses sector boundary
uint32_t sector_boundaries_crossed = 1 + (delta - bytes_to_sector_end) / ctrl->bytes_per_sector; // 1: boundary crossed; further boundaries crossed based on delta
uint32_t delta_sector_from_clusters_first_sector = seek_hint->sector_of_cluster + sector_boundaries_crossed;
uint32_t cluster_boundaries_crossed = delta_sector_from_clusters_first_sector / ctrl->sectors_per_cluster; // number of cluster boundary crossing
if (cluster_boundaries_crossed > 0) { // if we crossed a cluster boundary, then load the next cluster's first sector in chain
uint32_t cluster = seek_hint->cluster;
for (uint32_t i = 0; i < cluster_boundaries_crossed; i++) { // do the cluster crossings
cluster = fat32_get_next_chained_cluster(ctrl, cluster); // get next cluster
}
// update position fields
seek_hint->cluster = cluster;
seek_hint->sector_of_cluster = delta_sector_from_clusters_first_sector % ctrl->sectors_per_cluster;
seek_hint->sector += sector_boundaries_crossed;
seek_hint->pos = pos;
seek_hint->sector_begin_pos += sector_boundaries_crossed * ctrl->bytes_per_sector;
}
}
} else { // if seeking backwards, then no acceleration is available
const Fat32_FileEntry * entry = fat32_get_file_entry_by_byte_address(ctrl, file_helper->entry_ba); // fetch file entry
uint32_t first_cluster = fat32_get_first_cluster(entry); // get first cluster of the entry
uint32_t cluster = fat32_get_cluster_from_pos(ctrl, first_cluster, pos); // get cluster sequence number of the file using the read position
uint32_t first_sector = fat32_get_first_sector_of_cluster(ctrl, cluster); // get the first sector of the cluster
uint32_t bytes_per_cluster = fat32_get_bytes_per_cluster(ctrl); // get number of bytes per cluster
uint32_t pos_in_cluster = pos % bytes_per_cluster; // transform read position to a relative read position from the cluster beginning
uint32_t sector_of_cluster = pos_in_cluster / ctrl->bytes_per_sector; // get the sequence number of sector in the cluster that will be read
uint32_t sector = first_sector + sector_of_cluster; // offset the sectors based on relative read position
// update position fields
seek_hint->pos = pos;
seek_hint->sector_of_cluster = sector_of_cluster;
seek_hint->cluster = cluster;
seek_hint->sector = first_sector + sector_of_cluster;
seek_hint->sector_begin_pos = pos - (pos % ctrl->bytes_per_sector);
}
return 0;
}
uint32_t fat32_read_file_stream(const Fat32_CtrlBlock *ctrl, Fat32_FileHelper *file_helper, uint32_t len, uint8_t *p) {
FETCH_MSTG;
Fat32_SeekHint * seek_hint = &file_helper->last_pos;
uint32_t pos_in_sector = seek_hint->pos - seek_hint->sector_begin_pos; // determine position in current sector
READ_MSTG_TO_FIRST_BUFFER(seek_hint->sector);
// cap length, don't read after the end of the file
uint32_t read_len = MIN(len, file_helper->size - seek_hint->pos);
uint32_t offset = 0;
// reading from a file may involve reading across cluster boundaries
// 1. read the beginning from the first cluster
uint32_t first_read_len = MIN(read_len, ctrl->bytes_per_sector - seek_hint->pos);
memcpy(p, buffer + pos_in_sector, first_read_len);
read_len -= first_read_len; // decrease the total read len with the number of bytes read from the first sector of the file
offset += first_read_len; // advance offset
seek_hint->pos += first_read_len; // advance seek pos
// 2. read from intermediate sectors and read from the final sector
// if the remaining read length is non-zero, then maintain buffered sectors
while (read_len > 0) {
seek_hint->sector_of_cluster++; // increase sector index
if (seek_hint->sector_of_cluster >= ctrl->sectors_per_cluster) { // if we crossed a cluster boundary, then load the next cluster's first sector in chain
seek_hint->cluster = fat32_get_next_chained_cluster(ctrl, seek_hint->cluster); // get next cluster
seek_hint->sector = fat32_get_first_sector_of_cluster(ctrl, seek_hint->cluster);
seek_hint->sector_of_cluster = 0; // it's the zeroth sector of the cluster
READ_MSTG_TO_FIRST_BUFFER(seek_hint->sector); // load the newly determined cluster's first sector
} else { // if we didn't cross a cluster boundary, then...
seek_hint->sector++; //...just increase the sector count
READ_MSTG_TO_FIRST_BUFFER(seek_hint->sector); // ...and load that sector
}
seek_hint->sector_begin_pos += ctrl->bytes_per_sector; // advance begin position, we have stepped one sector forward
// copy the whole sector or some portion of the sector counting from the sector's begin
uint32_t copy_size = MIN(ctrl->bytes_per_sector, read_len);
memcpy(p + offset, buffer, copy_size);
read_len -= copy_size;
offset += copy_size;
seek_hint->pos += copy_size;
}
return read_len;
}

View File

@ -95,21 +95,29 @@ typedef struct {
uint32_t pos; // byte position uint32_t pos; // byte position
uint32_t cluster; // cluster corresponding to the position uint32_t cluster; // cluster corresponding to the position
uint32_t sector; // sector of the last read uint32_t sector; // sector of the last read
uint32_t sector_of_cluster; // sequence number of sector in the current cluster
uint32_t sector_begin_pos; // byte position of the sector's begin uint32_t sector_begin_pos; // byte position of the sector's begin
} Fat32_SeekHint; } Fat32_SeekHint;
typedef struct { typedef struct {
Fat32_CtrlBlock * ctrl; // pointer FAT32 control block const Fat32_CtrlBlock * ctrl; // pointer FAT32 control block
Fat32_SeekHint last_pos; // last read position Fat32_SeekHint last_pos; // last read position
uint32_t entry_pos; // FileEntry byte position uint32_t entry_ba; // FileEntry byte address on the mass storage
uint32_t size; // file size
} Fat32_FileHelper; } Fat32_FileHelper;
int fat32_load(Fat32_CtrlBlock *ctrl, uint32_t bpb_s, const MassStorage * mstg); int fat32_load(Fat32_CtrlBlock *ctrl, uint32_t bpb_s, const MassStorage * mstg);
int fat32_list_dir(const Fat32_CtrlBlock *ctrl, const char * dir); int fat32_list_dir(const Fat32_CtrlBlock *ctrl, const char * dir);
uint32_t fat32_read_file(const Fat32_CtrlBlock * ctrl, const Fat32_FileEntry * entry, uint32_t pos, uint32_t len, uint8_t * p); uint32_t fat32_read_file_random_access(const Fat32_CtrlBlock * ctrl, const Fat32_FileEntry * entry, uint32_t pos, uint32_t len, uint8_t * p);
const Fat32_FileEntry * fat32_locate_file(const Fat32_CtrlBlock *ctrl, const char *path); const Fat32_FileEntry * fat32_locate_file(const Fat32_CtrlBlock *ctrl, const char *path, uint32_t * file_entry_ba);
int fat32_open_file(const Fat32_CtrlBlock *ctrl, const char *path, Fat32_FileHelper *file_helper);
uint32_t fat32_seek_stream(const Fat32_CtrlBlock *ctrl, uint32_t pos, Fat32_FileHelper *file_helper);
uint32_t fat32_read_file_stream(const Fat32_CtrlBlock *ctrl, Fat32_FileHelper *file_helper, uint32_t len, uint8_t *p);
#endif //EMBPART_FAT32_H #endif //EMBPART_FAT32_H