From f2f4c34760de661f1dfe33c06760053692fece90 Mon Sep 17 00:00:00 2001 From: Vito Caputo Date: Sun, 8 Nov 2020 16:15:52 -0800 Subject: cache: introduce rudimentary CacheFile API Throughout the existing cache-related code there's constant checking if compression is active or not. This commit introduces a CacheFile ADT with the intention of replacing all the open coded stuff with simple calls into rmdCacheFile* functions operating on CacheFile instances. The management of split up cache files has also been implemented behind this API, so the reads and writes will transparently handle the split files. These have been named "chapters" in new code. No callers have been changed, this only adds the new facility. A subsequent commit will migrate things over. --- src/rmd_cache.c | 211 +++++++++++++++++++++++++++++++++++++++++++++++++------- src/rmd_cache.h | 4 ++ src/rmd_types.h | 16 +++++ 3 files changed, 207 insertions(+), 24 deletions(-) diff --git a/src/rmd_cache.c b/src/rmd_cache.c index bef6a83..94c0b68 100644 --- a/src/rmd_cache.c +++ b/src/rmd_cache.c @@ -37,6 +37,107 @@ #include #include +#define CACHE_FILE_SIZE_LIMIT (500 * 1024 * 1024) + + +/* open file @ path storing the file handles in *file, + * this doesn't store the new path since it's primarily + * for the purposes of opening new chapters for the same + * base path. + */ +static int _rmdCacheFileOpen(CacheFile *file, const char *path) +{ + const char *modestr = "rb"; + + assert(file); + + if (file->mode == RMD_CACHE_FILE_MODE_WRITE) { + modestr = "wb"; + + if (file->compressed) + modestr = "wb0f"; + } + + if (file->compressed) { + file->gzfp = gzopen(path, modestr); + } else { + file->fp = fopen(path, modestr); + } + + if (!file->gzfp && !file->fp) + return -1; + + file->chapter_n_bytes = 0; + + return 0; +} + + +/* only close the internal file handle, but don't free file */ +static int _rmdCacheFileClose(CacheFile *file) +{ + assert(file); + + /* TODO: return meaningful -errno on errors? */ + if (file->gzfp) { + if (gzclose(file->gzfp) != Z_OK) + return -1; + + file->gzfp = NULL; + } else if (file->fp) { + if (fclose(file->fp)) + return -1; + + file->fp = NULL; + } + + return 0; +} + + +/* open a CacheFile @ path, in the specified mode, returns NULL on error */ +CacheFile * rmdCacheFileOpen(ProgData *pdata, const char *path, CacheFileMode mode) +{ + CacheFile *f; + + assert(pdata); + assert(path); + + f = calloc(1, sizeof(*f)); + if (!f) + return NULL; + + f->path = strdup(path); + if (!f->path) { + free(f); + return NULL; + } + + f->mode = mode; + if (!pdata->args.zerocompression) + f->compressed = 1; + + if (_rmdCacheFileOpen(f, path) < 0) + return NULL; + + return f; +} + + +/* close a CacheFile, returns < 0 on error */ +int rmdCacheFileClose(CacheFile *file) +{ + assert(file); + + if (_rmdCacheFileClose(file) < 0) + return -1; + + free(file->path); + free(file); + + return 0; +} + /** *Construct an number postfixed name @@ -48,7 +149,7 @@ * \n number to be used as a postfix * */ -static void rmdCacheFileN(char *name, char **newname, int n) // Nth cache file +static void _rmdCacheFileN(char *name, char **newname, int n) // Nth cache file { char numbuf[8]; @@ -58,42 +159,104 @@ static void rmdCacheFileN(char *name, char **newname, int n) // Nth cache file strcat(*newname, numbuf); } -int rmdSwapCacheFilesWrite(char *name, int n, gzFile *fp, FILE **ucfp) + +/* read from a CacheFile, identical to gzread() */ +ssize_t rmdCacheFileRead(CacheFile *file, void *ptr, size_t len) { - char *newname = malloc(strlen(name) + 10); + ssize_t ret, read_n_bytes = 0; + + assert(file); + assert(ptr); - rmdCacheFileN(name, &newname, n); - if (*fp == NULL) { - fflush(*ucfp); - fclose(*ucfp); - *ucfp = fopen(newname, "wb"); + assert(file->mode == RMD_CACHE_FILE_MODE_READ); + +retry: + if (file->gzfp) { + int r; + + r = gzread(file->gzfp, ptr + read_n_bytes, len); + if (r < 0) + return -1; + + ret = r; } else { - gzflush(*fp, Z_FINISH); - gzclose(*fp); - *fp = gzopen(newname, "wb0f"); + ret = fread(ptr + read_n_bytes, 1, len, file->fp); + } + + read_n_bytes += ret; + file->chapter_n_bytes += read_n_bytes; + file->total_n_bytes += read_n_bytes; + + if (ret < len) { + char *newpath = malloc(strlen(file->path) + 10); + + len -= ret; + + if (_rmdCacheFileClose(file) < 0) { + free(newpath); + return -1; + } + + /* look for next chapter */ + _rmdCacheFileN(file->path, &newpath, file->chapter + 1); + if (_rmdCacheFileOpen(file, newpath) == 0) { + file->chapter++; + free(newpath); + goto retry; + } + + free(newpath); } - free(newname); - return ((*fp == NULL) && (*ucfp == NULL)); + return read_n_bytes; } -int rmdSwapCacheFilesRead(char *name, int n, gzFile *fp, FILE **ucfp) + +/* write to a CacheFile, identical to gzwrite() */ +ssize_t rmdCacheFileWrite(CacheFile *file, const void *ptr, size_t len) { - char *newname = malloc(strlen(name) + 10); + ssize_t ret; + + assert(file); + assert(ptr); + + assert(file->mode == RMD_CACHE_FILE_MODE_WRITE); + + /* transparently open next chapter if needed */ + if (file->chapter_n_bytes > CACHE_FILE_SIZE_LIMIT) { + char *newpath = malloc(strlen(file->path) + 10); + + if (_rmdCacheFileClose(file) < 0) + return -1; - rmdCacheFileN(name, &newname, n); - if (*fp == NULL) { - fclose(*ucfp); - *ucfp = fopen(newname, "rb"); + file->chapter++; + + _rmdCacheFileN(file->path, &newpath, file->chapter); + if (_rmdCacheFileOpen(file, newpath) < 0) { + free(newpath); + return -1; + } + } + + if (file->gzfp) { + int r; + + r = gzwrite(file->gzfp, ptr, len); + if (r < 0) + return -1; + + ret = r; } else { - gzclose(*fp); - *fp = gzopen(newname, "rb"); + ret = fwrite(ptr, 1, len, file->fp); } - free(newname); - return ((*fp == NULL) && (*ucfp == NULL)); + file->chapter_n_bytes += ret; + file->total_n_bytes += ret; + + return ret; } + int rmdPurgeCache(CacheData *cache_data_t, int sound) { struct stat buff; @@ -109,7 +272,7 @@ int rmdPurgeCache(CacheData *cache_data_t, int sound) fprintf(stderr, "Couldn't remove temporary file %s", cache_data_t->imgdata); exit_value = 1; } - rmdCacheFileN(cache_data_t->imgdata, &fname, nth_cache); + _rmdCacheFileN(cache_data_t->imgdata, &fname, nth_cache); nth_cache++; } diff --git a/src/rmd_cache.h b/src/rmd_cache.h index 9f4acce..cfa6cae 100644 --- a/src/rmd_cache.h +++ b/src/rmd_cache.h @@ -34,6 +34,10 @@ #include +CacheFile * rmdCacheFileOpen(ProgData *pdata, const char *path, CacheFileMode mode); +int rmdCacheFileClose(CacheFile *file); +ssize_t rmdCacheFileRead(CacheFile *file, void *ptr, size_t len); +ssize_t rmdCacheFileWrite(CacheFile *file, const void *ptr, size_t len); /** * Change file pointer to a new file while writting diff --git a/src/rmd_types.h b/src/rmd_types.h index 1dc5f14..3cc1a84 100644 --- a/src/rmd_types.h +++ b/src/rmd_types.h @@ -184,6 +184,22 @@ typedef struct _EncData{ FILE *fp; } EncData; +/* CacheFiles abstract compressed vs. uncompressed files */ +typedef enum CacheFileMode { + RMD_CACHE_FILE_MODE_WRITE, + RMD_CACHE_FILE_MODE_READ, +} CacheFileMode; + +typedef struct CacheFile { + char *path; + unsigned chapter; + size_t chapter_n_bytes, total_n_bytes; + unsigned compressed:1; + CacheFileMode mode; + gzFile gzfp; + FILE *fp; +} CacheFile; + //this struct will hold a few basic //information, needed for caching the frames. typedef struct _CacheData{ -- cgit v1.2.1