summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorXavier Del Campo Romero <xavi.dcr@tutanota.com>2021-10-24 02:57:40 +0200
committerXavier Del Campo Romero <xavi.dcr@tutanota.com>2021-10-24 03:10:03 +0200
commita34280dec778b7f6ba1e68450aee1c5b543aeda8 (patch)
treea56dc2569f49d8b771f5a117649241b1eef57c2a
parent9c6112eb5bec5e66bff092e8131839f38a2be39b (diff)
Provide buffered fread(3)
In order to reduce calls to seek(2) (which are very expensive), this commit keeps a sector-sized cache for each available FILE instance, except from the standard streams, of course. As expected, this allows calls to fread(3) to react much faster when small, contiguous chunks are read.
-rw-r--r--libpsx/include/stdio.h8
-rw-r--r--libpsx/src/libc.c188
2 files changed, 134 insertions, 62 deletions
diff --git a/libpsx/include/stdio.h b/libpsx/include/stdio.h
index 456a438..89d8817 100644
--- a/libpsx/include/stdio.h
+++ b/libpsx/include/stdio.h
@@ -70,6 +70,12 @@ typedef struct
unsigned int eof;
/** Error marker */
unsigned int error;
+ /** Sector buffer. */
+ unsigned char *const buf;
+ /** Last used sector for reading. */
+ size_t last_sect;
+ /** Sector buffer can be used for reading. */
+ unsigned int cache_available;
}FILE;
extern FILE *const stdin, *const stdout, *const stderr;
@@ -111,7 +117,7 @@ int vprintf(const char *fmt, va_list ap);
FILE *fdopen(int fildes, const char *mode);
FILE *fopen(const char *path, const char *mode);
int fclose(FILE *stream);
-int fread(void *ptr, int size, int nmemb, FILE *f);
+size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
size_t fwrite(void *ptr, size_t size, size_t nmemb, FILE *stream);
int fgetc(FILE *f);
diff --git a/libpsx/src/libc.c b/libpsx/src/libc.c
index 7480825..2cf4064 100644
--- a/libpsx/src/libc.c
+++ b/libpsx/src/libc.c
@@ -19,13 +19,14 @@
#include <errno.h>
#include <unistd.h>
-char onesec_buf[2048];
int errno;
int __stdio_direction = STDIO_DIRECTION_BIOS;
static unsigned int __sio_cr_mapped = 0;
#define NUM_OF_FILE_STRUCTS 6
+enum {SECTOR_SZ = 2048};
+
static FILE file_structs[NUM_OF_FILE_STRUCTS] =
{
[0] = // stdin
@@ -61,6 +62,11 @@ static FILE file_structs[NUM_OF_FILE_STRUCTS] =
.eof = 0,
.error = 0
},
+
+ [3 ... NUM_OF_FILE_STRUCTS - 1] =
+ {
+ .buf = (unsigned char[SECTOR_SZ]){}
+ }
};
FILE *const stdin = &file_structs[0];
@@ -134,10 +140,20 @@ FILE *fdopen(int fildes, const char *mode)
// Find a free file structure
for(x = 0; x < NUM_OF_FILE_STRUCTS; x++)
{
- if(file_structs[x].used == 0)
+ FILE *const f = &file_structs[x];
+
+ if(f->used == 0)
{
- bzero(&file_structs[x], sizeof(FILE));
- file_structs[x].used = 1;
+ f->size = 0;
+ f->eof = 0;
+ f->error = 0;
+ f->used = 1;
+ f->last_sect = 0;
+ f->cache_available = 0;
+
+ if (f->buf)
+ memset(f->buf, 0, SECTOR_SZ);
+
break;
}
}
@@ -230,90 +246,140 @@ int fclose(FILE *stream)
return 0;
}
-/*
- * fread doesn't require reads to be carried in block unit
- * Notice that however seeks on the CD drive will be very slow - so avoid using non block units
- *
- * This is done to make programming and porting easier
- */
-
-int fread(void *ptr, int size, int nmemb, FILE *f)
+static size_t cdromread(void *restrict ptr, size_t sz,
+ FILE *restrict const f)
{
- int rsize = size * nmemb;
- int csize = rsize;
- int max;
- int nsect = (f->pos + rsize) >> 11;
- nsect -= f->pos >> 11;
- nsect++;
+ const size_t start_sect = f->pos >> __builtin_ctz(SECTOR_SZ);
+ const size_t end_pos = f->pos + sz;
+ const size_t end_sector = end_pos >> __builtin_ctz(SECTOR_SZ);
+ size_t readb = 0;
+ int read_complete_sector = 0;
- //printf("f->dev = %d, f->pos = %d, rsize = %d\n", f->dev, f->pos, rsize);
-
- if(f->dev == FDEV_CDROM)
+ if (feof(f) || ferror(f))
+ return 0;
+ else if (f->cache_available)
{
- // First sector
- lseek(f->fildes, f->pos & (~0x7ff), SEEK_SET);
- read(f->fildes, onesec_buf, 2048);
+ const size_t first_sector_sz = f->pos % SECTOR_SZ;
- max = 2048 - (f->pos & 2047);
+ if (f->last_sect == start_sect && f->last_sect == end_sector)
+ {
+ /* Use buffered I/O. */
+ size_t max;
- //printf("ptr(FIRST) = %d, %x\n", ptr, ptr);
- printf("rsize = %d\n", rsize);
+ if (end_pos >= f->size)
+ {
+ max = f->size - f->pos;
+ f->eof = 1;
+ }
+ else
+ {
+ max = sz;
+ }
- memcpy(ptr, onesec_buf + (f->pos & 2047), (rsize > max) ? max : rsize);
+ memcpy(ptr, &f->buf[first_sector_sz], max);
+ f->pos += max;
+ readb = max;
+ }
+ else if (f->last_sect == start_sect)
+ {
+ /* Partial buffered I/O possible. */
+ const size_t firstsz = SECTOR_SZ - first_sector_sz;
+
+ memcpy(ptr, &f->buf[first_sector_sz], firstsz);
+ f->pos += firstsz;
+ lseek(f->fildes, f->pos, SEEK_SET);
+ f->cache_available = 0;
+ readb += firstsz;
+ read_complete_sector = 1;
+ }
+ }
- // Middle sector
- ptr += max;
+ const size_t rem = sz - readb;
- //printf("ptr(MIDDLEsex) = %d, %x\n", ptr, ptr);
- nsect--;
- csize -= max;
+ if (rem)
+ {
+ const size_t whole_sectors = rem / SECTOR_SZ;
- if(nsect > 1)
+ if (whole_sectors)
{
- //lseek(f->fildes, (f->pos & (~0x7ff)) + 2048, SEEK_SET);
+ if (!read_complete_sector)
+ lseek(f->fildes, f->pos & ~(SECTOR_SZ - 1), SEEK_SET);
-//#warning "Check correctness of this calculation."
+ const size_t s = whole_sectors * SECTOR_SZ;
+ const int n = read(f->fildes, &((unsigned char *)ptr)[readb], s);
- /*if(rsize & 2047)
- sect_num = (rsize|2047)+1;
- else
- sect_num = rsize;
+ if (n >= 0)
+ {
+ readb += s;
+ f->pos += s;
+ ptr = (unsigned char *)ptr + s;
- sect_num -= 4096;*/
+ if (f->pos >= f->size)
+ f->eof = 1;
- //printf("read_middle=%d, sect_num = %d\n", read(f->fildes, ptr, sect_num), sect_num);
+ f->cache_available = 1;
+ }
+ }
- read(f->fildes, ptr, (nsect - 1) * 2048);
+ if (f->pos < f->size)
+ {
+ int n;
- ptr += (nsect - 1) * 2048;
- csize -= (nsect - 1) * 2048;
- nsect = 1;
- }
+ if (!read_complete_sector)
+ lseek(f->fildes, f->pos & ~(SECTOR_SZ - 1), SEEK_SET);
- //printf("ptr(LAST) = %d, %x\n", ptr, ptr);
+ n = read(f->fildes, f->buf, SECTOR_SZ);
- if(nsect == 1)
- {
- // Last sector
- read(f->fildes, onesec_buf, 2048);
+ if (n >= 0)
+ {
+ readb = n > rem ? rem : n;
+
+ memmove(ptr, f->buf, readb);
+ ptr = (unsigned char *)ptr + readb;
+ f->pos += readb;
+
+ if (f->pos >= f->size)
+ f->eof = 1;
- memcpy(ptr, onesec_buf, csize);
+ f->cache_available = 1;
+ }
}
}
- else if(f->dev == FDEV_CONSOLE)
- return 0;
+ f->last_sect = end_sector;
+ return f->pos;
+}
+
+/*
+ * fread doesn't require reads to be carried in block unit
+ * Notice that however seeks on the CD drive will be very slow - so avoid using non block units
+ *
+ * This is done to make programming and porting easier
+ */
- if(f->dev != FDEV_CONSOLE)
+size_t fread(void *restrict const ptr, const size_t size, const size_t nmemb,
+ FILE *restrict const f)
+{
+ if (size && nmemb)
{
- f->pos+= rsize;
- f->eof = (f->pos > f->size);
+ size_t rsize = size * nmemb;
+
+ switch (f->dev)
+ {
+ case FDEV_CDROM:
+ rsize = cdromread(ptr, rsize, f);
+ break;
+
+ case FDEV_CONSOLE:
+ /* Fall through. */
+ default:
+ return 0;
+ }
- if(f->eof)
- f->pos = f->size;
+ return rsize / size;
}
- return rsize;
+ return 0;
}
int fgetc(FILE *f)