From 3ad51e49e4c1cfe2cbfafc27d3a73dd904b00578 Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Sun, 1 Dec 2002 11:21:27 +0000 Subject: [PATCH] Added buffering API. --- physfs.c | 181 ++++++++++++++++++++++++++++-- physfs.h | 110 +++++++++++++++--- physfs_internal.h | 25 +++++ test/test_physfs.c | 274 ++++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 561 insertions(+), 29 deletions(-) diff --git a/physfs.c b/physfs.c index 9e12cf62..de7956b1 100644 --- a/physfs.c +++ b/physfs.c @@ -1227,12 +1227,22 @@ int __PHYSFS_verifySecurity(DirHandle *h, const char *fname) if (h->funcs->isSymLink(h, str, &retval)) { __PHYSFS_setError(ERR_SYMLINK_DISALLOWED); - retval = 0; + free(str); + return(0); /* insecure. */ } /* if */ - /* break out early if path element is missing or it's a symlink. */ + /* break out early if path element is missing. */ if (!retval) + { + /* + * We need to clear it if it's the last element of the path, + * since this might be a non-existant file we're opening + * for writing... + */ + if (end == NULL) + retval = 1; break; + } /* if */ } /* if */ if (end == NULL) @@ -1569,6 +1579,9 @@ static PHYSFS_file *doOpenWrite(const char *fname, int appending) free(list); else { + rc->buffer = NULL; /* just in case. */ + rc->buffill = rc->bufpos = rc->bufsize = 0; /* just in case. */ + rc->forReading = 0; list->handle.opaque = (void *) rc; list->next = openWriteList; openWriteList = list; @@ -1615,13 +1628,17 @@ PHYSFS_file *PHYSFS_openRead(const char *fname) BAIL_IF_MACRO_MUTEX(rc == NULL, NULL, stateLock, NULL); list = (FileHandleList *) malloc(sizeof (FileHandleList)); - BAIL_IF_MACRO(!list, ERR_OUT_OF_MEMORY, NULL); + BAIL_IF_MACRO_MUTEX(!list, ERR_OUT_OF_MEMORY, stateLock, NULL); list->handle.opaque = (void *) rc; list->next = openReadList; openReadList = list; retval = &(list->handle); - __PHYSFS_platformReleaseMutex(stateLock); + + rc->buffer = NULL; /* just in case. */ + rc->buffill = rc->bufpos = rc->bufsize = 0; /* just in case. */ + rc->forReading = 1; + return(retval); } /* PHYSFS_openRead */ @@ -1631,16 +1648,22 @@ static int closeHandleInOpenList(FileHandleList **list, PHYSFS_file *handle) FileHandle *h = (FileHandle *) handle->opaque; FileHandleList *prev = NULL; FileHandleList *i; - int rc; + int rc = 1; for (i = *list; i != NULL; i = i->next) { if (&i->handle == handle) /* handle is in this list? */ { - rc = h->funcs->fileClose(h); + PHYSFS_uint8 *tmp = h->buffer; + rc = PHYSFS_flush(handle); + if (rc) + rc = h->funcs->fileClose(h); if (!rc) return(-1); + if (tmp != NULL) /* free any associated buffer. */ + free(tmp); + if (prev == NULL) *list = i->next; else @@ -1677,18 +1700,92 @@ int PHYSFS_close(PHYSFS_file *handle) } /* PHYSFS_close */ +static PHYSFS_sint64 doBufferedRead(PHYSFS_file *handle, void *buffer, + PHYSFS_uint32 objSize, + PHYSFS_uint32 objCount) +{ + FileHandle *h = (FileHandle *) handle->opaque; + PHYSFS_sint64 retval = 0; + PHYSFS_uint32 remainder = 0; + + while (objCount > 0) + { + PHYSFS_uint64 buffered = h->buffill - h->bufpos; + PHYSFS_uint64 mustread = (objSize * objCount) - remainder; + PHYSFS_uint32 copied; + + if (buffered == 0) /* need to refill buffer? */ + { + PHYSFS_sint64 rc = h->funcs->read(h, h->buffer, 1, h->bufsize); + if (rc <= 0) + { + h->bufpos -= remainder; + return(((rc == -1) && (retval == 0)) ? -1 : retval); + } /* if */ + + buffered = h->buffill = rc; + h->bufpos = 0; + } /* if */ + + if (buffered > mustread) + buffered = mustread; + + memcpy(buffer, h->buffer + h->bufpos, (size_t) buffered); + buffer = ((PHYSFS_uint8 *) buffer) + buffered; + h->bufpos += buffered; + buffered += remainder; /* take remainder into account. */ + copied = (buffered / objSize); + remainder = (buffered % objSize); + retval += copied; + objCount -= copied; + } /* while */ + + return(retval); +} /* doBufferedRead */ + + PHYSFS_sint64 PHYSFS_read(PHYSFS_file *handle, void *buffer, PHYSFS_uint32 objSize, PHYSFS_uint32 objCount) { FileHandle *h = (FileHandle *) handle->opaque; + + BAIL_IF_MACRO(!h->forReading, ERR_FILE_ALREADY_OPEN_W, -1); + if (h->buffer != NULL) + return(doBufferedRead(handle, buffer, objSize, objCount)); + return(h->funcs->read(h, buffer, objSize, objCount)); } /* PHYSFS_read */ +static PHYSFS_sint64 doBufferedWrite(PHYSFS_file *handle, const void *buffer, + PHYSFS_uint32 objSize, + PHYSFS_uint32 objCount) +{ + FileHandle *h = (FileHandle *) handle->opaque; + + /* whole thing fits in the buffer? */ + if (h->buffill + (objSize * objCount) < h->bufsize) + { + memcpy(h->buffer + h->buffill, buffer, objSize * objCount); + h->buffill += (objSize * objCount); + return(objCount); + } /* if */ + + /* would overflow buffer. Flush and then write the new objects, too. */ + BAIL_IF_MACRO(!PHYSFS_flush(handle), NULL, -1); + return(h->funcs->write(h, buffer, objSize, objCount)); +} /* doBufferedWrite */ + + PHYSFS_sint64 PHYSFS_write(PHYSFS_file *handle, const void *buffer, PHYSFS_uint32 objSize, PHYSFS_uint32 objCount) { FileHandle *h = (FileHandle *) handle->opaque; + + BAIL_IF_MACRO(h->forReading, ERR_FILE_ALREADY_OPEN_R, -1); + if (h->buffer != NULL) + return(doBufferedWrite(handle, buffer, objSize, objCount)); + return(h->funcs->write(h, buffer, objSize, objCount)); } /* PHYSFS_write */ @@ -1696,20 +1793,29 @@ PHYSFS_sint64 PHYSFS_write(PHYSFS_file *handle, const void *buffer, int PHYSFS_eof(PHYSFS_file *handle) { FileHandle *h = (FileHandle *) handle->opaque; - return(h->funcs->eof(h)); + + if (!h->forReading) /* never EOF on files opened for write/append. */ + return(0); + + /* eof if buffer is empty and archiver says so. */ + return((h->bufpos == h->buffill) && (h->funcs->eof(h))); } /* PHYSFS_eof */ PHYSFS_sint64 PHYSFS_tell(PHYSFS_file *handle) { FileHandle *h = (FileHandle *) handle->opaque; - return(h->funcs->tell(h)); + PHYSFS_sint64 retval = h->forReading ? + (h->funcs->tell(h) - h->buffill) + h->bufpos : + (h->funcs->tell(h) + h->buffill); + return(retval); } /* PHYSFS_tell */ int PHYSFS_seek(PHYSFS_file *handle, PHYSFS_uint64 pos) { FileHandle *h = (FileHandle *) handle->opaque; + BAIL_IF_MACRO(!PHYSFS_flush(handle), NULL, 0); return(h->funcs->seek(h, pos)); } /* PHYSFS_seek */ @@ -1721,6 +1827,65 @@ PHYSFS_sint64 PHYSFS_fileLength(PHYSFS_file *handle) } /* PHYSFS_filelength */ +int PHYSFS_setBuffer(PHYSFS_file *handle, PHYSFS_uint64 bufsize) +{ + FileHandle *h = (FileHandle *) handle->opaque; + + BAIL_IF_MACRO(!PHYSFS_flush(handle), NULL, 0); + + /* + * For reads, we need to move the file pointer to where it would be + * if we weren't buffering, so that the next read will get the + * right chunk of stuff from the file. PHYSFS_flush() handles writes. + */ + if ((h->forReading) && (h->buffill != h->bufpos)) + { + PHYSFS_uint64 pos; + PHYSFS_sint64 curpos = h->funcs->tell(h); + BAIL_IF_MACRO(curpos == -1, NULL, 0); + pos = ((curpos - h->buffill) + h->bufpos); + BAIL_IF_MACRO(!h->funcs->seek(h, pos), NULL, 0); + } /* if */ + + if (bufsize == 0) /* delete existing buffer. */ + { + if (h->buffer != NULL) + { + free(h->buffer); + h->buffer = NULL; + } /* if */ + } /* if */ + + else + { + PHYSFS_uint8 *newbuf = realloc(h->buffer, bufsize); + BAIL_IF_MACRO(newbuf == NULL, ERR_OUT_OF_MEMORY, 0); + h->buffer = newbuf; + } /* else */ + + h->bufsize = bufsize; + h->buffill = h->bufpos = 0; + return(1); +} /* PHYSFS_setBuffer */ + + +int PHYSFS_flush(PHYSFS_file *handle) +{ + FileHandle *h = (FileHandle *) handle->opaque; + PHYSFS_sint64 rc; + + if ((h->forReading) || (h->bufpos == h->buffill)) + return(1); /* open for read or buffer empty are successful no-ops. */ + + /* dump buffer to disk. */ + rc = h->funcs->write(h, h->buffer + h->bufpos, h->buffill - h->bufpos, 1); + BAIL_IF_MACRO(rc <= 0, NULL, 0); + + h->bufpos = h->buffill = 0; + return(1); +} /* PHYSFS_flush */ + + LinkedStringList *__PHYSFS_addToLinkedStringList(LinkedStringList *retval, LinkedStringList **prev, const char *str, diff --git a/physfs.h b/physfs.h index 3efe3b2d..34892479 100644 --- a/physfs.h +++ b/physfs.h @@ -258,6 +258,8 @@ PHYSFS_COMPILE_TIME_ASSERT(sint64, sizeof(PHYSFS_sint64) == 8); * \sa PHYSFS_seek * \sa PHYSFS_tell * \sa PHYSFS_eof + * \sa PHYSFS_setBuffer + * \sa PHYSFS_flush */ typedef struct { @@ -289,6 +291,7 @@ typedef struct const char *url; /**< URL related to this archive */ } PHYSFS_ArchiveInfo; + /** * \struct PHYSFS_Version * \brief Information the version of PhysicsFS in use. @@ -315,6 +318,9 @@ typedef struct #define PHYSFS_VER_PATCH 7 #endif /* DOXYGEN_SHOULD_IGNORE_THIS */ + +/* PhysicsFS state stuff ... */ + /** * \def PHYSFS_VERSION(x) * \brief Macro to determine PhysicsFS version program was compiled against. @@ -775,6 +781,8 @@ __EXPORT__ int PHYSFS_setSaneConfig(const char *organization, int archivesFirst); +/* Directory management stuff ... */ + /** * \fn int PHYSFS_mkdir(const char *dirName) * \brief Create a directory. @@ -856,7 +864,6 @@ __EXPORT__ int PHYSFS_delete(const char *filename); __EXPORT__ const char *PHYSFS_getRealDir(const char *filename); - /** * \fn char **PHYSFS_enumerateFiles(const char *dir) * \brief Get a file listing of a search path's directory. @@ -956,6 +963,24 @@ __EXPORT__ int PHYSFS_isDirectory(const char *fname); __EXPORT__ int PHYSFS_isSymbolicLink(const char *fname); +/** + * \fn PHYSFS_sint64 PHYSFS_getLastModTime(const char *filename) + * \brief Get the last modification time of a file. + * + * The modtime is returned as a number of seconds since the epoch + * (Jan 1, 1970). The exact derivation and accuracy of this time depends on + * the particular archiver. If there is no reasonable way to obtain this + * information for a particular archiver, or there was some sort of error, + * this function returns (-1). + * + * \param filename filename to check, in platform-independent notation. + * \return last modified time of the file. -1 if it can't be determined. + */ +__EXPORT__ PHYSFS_sint64 PHYSFS_getLastModTime(const char *filename); + + +/* i/o stuff... */ + /** * \fn PHYSFS_file *PHYSFS_openWrite(const char *filename) * \brief Open a file for writing. @@ -1053,22 +1078,6 @@ __EXPORT__ PHYSFS_file *PHYSFS_openRead(const char *filename); __EXPORT__ int PHYSFS_close(PHYSFS_file *handle); -/** - * \fn PHYSFS_sint64 PHYSFS_getLastModTime(const char *filename) - * \brief Get the last modification time of a file. - * - * The modtime is returned as a number of seconds since the epoch - * (Jan 1, 1970). The exact derivation and accuracy of this time depends on - * the particular archiver. If there is no reasonable way to obtain this - * information for a particular archiver, or there was some sort of error, - * this function returns (-1). - * - * \param filename filename to check, in platform-independent notation. - * \return last modified time of the file. -1 if it can't be determined. - */ -__EXPORT__ PHYSFS_sint64 PHYSFS_getLastModTime(const char *filename); - - /** * \fn PHYSFS_sint64 PHYSFS_read(PHYSFS_file *handle, void *buffer, PHYSFS_uint32 objSize, PHYSFS_uint32 objCount) * \brief Read data from a PhysicsFS filehandle @@ -1108,6 +1117,9 @@ __EXPORT__ PHYSFS_sint64 PHYSFS_write(PHYSFS_file *handle, PHYSFS_uint32 objSize, PHYSFS_uint32 objCount); + +/* File position stuff... */ + /** * \fn int PHYSFS_eof(PHYSFS_file *handle) * \brief Check for end-of-file state on a PhysicsFS filehandle. @@ -1172,6 +1184,70 @@ __EXPORT__ int PHYSFS_seek(PHYSFS_file *handle, PHYSFS_uint64 pos); __EXPORT__ PHYSFS_sint64 PHYSFS_fileLength(PHYSFS_file *handle); +/* Buffering stuff... */ + +/** + * \fn int PHYSFS_setBuffer(PHYSFS_file *handle, PHYSFS_uint64 bufsize) + * \brief Set up buffering for a PhysicsFS file handle. + * + * Define an i/o buffer for a file handle. A memory block of (bufsize) bytes + * will be allocated and associated with (handle). + * + * For files opened for reading, up to (bufsize) bytes are read from (handle) + * and stored in the internal buffer. Calls to PHYSFS_read() will pull + * from this buffer until it is empty, and then refill it for more reading. + * Note that compressed files, like ZIP archives, will decompress while + * buffering, so this can be handy for offsetting CPU-intensive operations. + * The buffer isn't filled until you do your next read. + * + * For files opened for writing, data will be buffered to memory until the + * buffer is full or the buffer is flushed. Closing a handle implicitly + * causes a flush...check your return values! + * + * Seeking, etc transparently accounts for buffering. + * + * You can resize an existing buffer by calling this function more than once + * on the same file. Setting the buffer size to zero will free an existing + * buffer. + * + * PhysicsFS file handles are unbuffered by default. + * + * Please check the return value of this function! Failures can include + * not being able to seek backwards in a read-only file when removing the + * buffer, not being able to allocate the buffer, and not being able to + * flush the buffer to disk, among other unexpected problems. + * + * \param handle handle returned from PHYSFS_open*(). + * \param bufsize size, in bytes, of buffer to allocate. + * \return nonzero if successful, zero on error. + * + * \sa PHYSFS_flush + * \sa PHYSFS_read + * \sa PHYSFS_write + * \sa PHYSFS_close + */ +__EXPORT__ int PHYSFS_setBuffer(PHYSFS_file *handle, PHYSFS_uint64 bufsize); + + +/** + * \fn int PHYSFS_flush(PHYSFS_file *handle, PHYSFS_uint64 bufsize) + * \brief Flush a buffered PhysicsFS file handle. + * + * For buffered files opened for writing, this will put the current contents + * of the buffer to disk and flag the buffer as empty if possible. + * + * For buffered files opened for reading or unbuffered files, this is a safe + * no-op, and will report success. + * + * \param handle handle returned from PHYSFS_open*(). + * \return nonzero if successful, zero on error. + * + * \sa PHYSFS_setBuffer + * \sa PHYSFS_close + */ +__EXPORT__ int PHYSFS_flush(PHYSFS_file *handle); + + /* Byteorder stuff... */ /** diff --git a/physfs_internal.h b/physfs_internal.h index 2fa6ee06..311d701d 100644 --- a/physfs_internal.h +++ b/physfs_internal.h @@ -724,6 +724,31 @@ typedef struct __PHYSFS_FILEHANDLE__ */ void *opaque; + /* + * Non-zero if file opened for reading, zero if write/append. + */ + PHYSFS_uint8 forReading; + + /* + * This is the buffer, if one is set (NULL otherwise). Don't touch. + */ + PHYSFS_uint8 *buffer; + + /* + * This is the buffer size, if one is set (0 otherwise). Don't touch. + */ + PHYSFS_uint64 bufsize; + + /* + * This is the buffer fill size. Don't touch. + */ + PHYSFS_uint64 buffill; + + /* + * This is the buffer position. Don't touch. + */ + PHYSFS_uint64 bufpos; + /* * This should be the DirHandle that created this FileHandle. */ diff --git a/test/test_physfs.c b/test/test_physfs.c index ee67d059..d6061749 100644 --- a/test/test_physfs.c +++ b/test/test_physfs.c @@ -34,6 +34,7 @@ #define TEST_VERSION_PATCH 7 static FILE *history_file = NULL; +static PHYSFS_uint32 do_buffer_size = 0; static void output_versions(void) { @@ -288,6 +289,230 @@ static int cmd_permitsyms(char *args) } /* cmd_permitsyms */ +static int cmd_setbuffer(char *args) +{ + if (*args == '\"') + { + args++; + args[strlen(args) - 1] = '\0'; + } /* if */ + + do_buffer_size = atoi(args); + if (do_buffer_size) + { + printf("Further tests will set a (%lu) size buffer.\n", + (unsigned long) do_buffer_size); + } /* if */ + + else + { + printf("Further tests will NOT use a buffer.\n"); + } /* else */ + + return(1); +} /* cmd_setbuffer */ + + +static int cmd_stressbuffer(char *args) +{ + int num; + + if (*args == '\"') + { + args++; + args[strlen(args) - 1] = '\0'; + } /* if */ + + num = atoi(args); + if (num < 0) + printf("buffer must be greater than or equal to zero.\n"); + else + { + PHYSFS_file *f; + int rndnum; + + printf("Stress testing with (%d) byte buffer...\n", num); + f = PHYSFS_openWrite("test.txt"); + if (f == NULL) + printf("Couldn't open test.txt for writing: %s.\n", PHYSFS_getLastError()); + else + { + int i, j; + char buf[37]; + char buf2[37]; + + if (!PHYSFS_setBuffer(f, num)) + { + printf("PHYSFS_setBuffer() failed: %s.\n", PHYSFS_getLastError()); + PHYSFS_close(f); + PHYSFS_delete("test.txt"); + return(1); + } /* if */ + + strcpy(buf, "abcdefghijklmnopqrstuvwxyz0123456789"); + srand(time(NULL)); + + for (i = 0; i < 10; i++) + { + for (j = 0; j < 10000; j++) + { + int right = 1 + (int) (35.0 * rand() / (RAND_MAX + 1.0)); + int left = 36 - right; + if (PHYSFS_write(f, buf, left, 1) != 1) + { + printf("PHYSFS_write() failed: %s.\n", PHYSFS_getLastError()); + PHYSFS_close(f); + return(1); + } /* if */ + + rndnum = 1 + (int) (1000.0 * rand() / (RAND_MAX + 1.0)); + if (rndnum == 42) + { + if (!PHYSFS_flush(f)) + { + printf("PHYSFS_flush() failed: %s.\n", PHYSFS_getLastError()); + PHYSFS_close(f); + return(1); + } /* if */ + } /* if */ + + if (PHYSFS_write(f, buf + left, 1, right) != right) + { + printf("PHYSFS_write() failed: %s.\n", PHYSFS_getLastError()); + PHYSFS_close(f); + return(1); + } /* if */ + + rndnum = 1 + (int) (1000.0 * rand() / (RAND_MAX + 1.0)); + if (rndnum == 42) + { + if (!PHYSFS_flush(f)) + { + printf("PHYSFS_flush() failed: %s.\n", PHYSFS_getLastError()); + PHYSFS_close(f); + return(1); + } /* if */ + } /* if */ + } /* for */ + + if (!PHYSFS_flush(f)) + { + printf("PHYSFS_flush() failed: %s.\n", PHYSFS_getLastError()); + PHYSFS_close(f); + return(1); + } /* if */ + + } /* for */ + + if (!PHYSFS_close(f)) + { + printf("PHYSFS_close() failed: %s.\n", PHYSFS_getLastError()); + return(1); /* oh well. */ + } /* if */ + + printf(" ... test file written ...\n"); + f = PHYSFS_openRead("test.txt"); + if (f == NULL) + { + printf("Failed to reopen stress file for reading: %s.\n", PHYSFS_getLastError()); + return(1); + } /* if */ + + if (!PHYSFS_setBuffer(f, num)) + { + printf("PHYSFS_setBuffer() failed: %s.\n", PHYSFS_getLastError()); + PHYSFS_close(f); + return(1); + } /* if */ + + for (i = 0; i < 10; i++) + { + for (j = 0; j < 10000; j++) + { + int right = 1 + (int) (35.0 * rand() / (RAND_MAX + 1.0)); + int left = 36 - right; + if (PHYSFS_read(f, buf2, left, 1) != 1) + { + printf("PHYSFS_read() failed: %s.\n", PHYSFS_getLastError()); + PHYSFS_close(f); + return(1); + } /* if */ + + rndnum = 1 + (int) (1000.0 * rand() / (RAND_MAX + 1.0)); + if (rndnum == 42) + { + if (!PHYSFS_flush(f)) + { + printf("PHYSFS_flush() failed: %s.\n", PHYSFS_getLastError()); + PHYSFS_close(f); + return(1); + } /* if */ + } /* if */ + + if (PHYSFS_read(f, buf2 + left, 1, right) != right) + { + printf("PHYSFS_read() failed: %s.\n", PHYSFS_getLastError()); + PHYSFS_close(f); + return(1); + } /* if */ + + rndnum = 1 + (int) (1000.0 * rand() / (RAND_MAX + 1.0)); + if (rndnum == 42) + { + if (!PHYSFS_flush(f)) + { + printf("PHYSFS_flush() failed: %s.\n", PHYSFS_getLastError()); + PHYSFS_close(f); + return(1); + } /* if */ + } /* if */ + + if (memcmp(buf, buf2, 36) != 0) + { + printf("readback is mismatched on iterations (%d, %d).\n", i, j); + printf("wanted: ["); + for (i = 0; i < 36; i++) + printf("%c", buf[i]); + printf("]\n"); + + printf(" got: ["); + for (i = 0; i < 36; i++) + printf("%c", buf2[i]); + printf("]\n"); + PHYSFS_close(f); + return(1); + } /* if */ + } /* for */ + + if (!PHYSFS_flush(f)) + { + printf("PHYSFS_flush() failed: %s.\n", PHYSFS_getLastError()); + PHYSFS_close(f); + return(1); + } /* if */ + + } /* for */ + + printf(" ... test file read ...\n"); + + if (!PHYSFS_eof(f)) + printf("PHYSFS_eof() returned true! That's wrong.\n"); + + if (!PHYSFS_close(f)) + { + printf("PHYSFS_close() failed: %s.\n", PHYSFS_getLastError()); + return(1); /* oh well. */ + } /* if */ + + PHYSFS_delete("test.txt"); + printf("stress test completed successfully.\n"); + } /* else */ + } /* else */ + + return(1); +} /* cmd_stressbuffer */ + + static int cmd_setsaneconfig(char *args) { char *org; @@ -433,6 +658,17 @@ static int cmd_cat(char *args) printf("failed to open. Reason: [%s].\n", PHYSFS_getLastError()); else { + if (do_buffer_size) + { + if (!PHYSFS_setBuffer(f, do_buffer_size)) + { + printf("failed to set file buffer. Reason: [%s].\n", + PHYSFS_getLastError()); + PHYSFS_close(f); + return(1); + } /* if */ + } /* if */ + while (1) { char buffer[128]; @@ -506,8 +742,22 @@ static int cmd_append(char *args) printf("failed to open. Reason: [%s].\n", PHYSFS_getLastError()); else { - size_t bw = strlen(WRITESTR); - PHYSFS_sint64 rc = PHYSFS_write(f, WRITESTR, 1, bw); + size_t bw; + PHYSFS_sint64 rc; + + if (do_buffer_size) + { + if (!PHYSFS_setBuffer(f, do_buffer_size)) + { + printf("failed to set file buffer. Reason: [%s].\n", + PHYSFS_getLastError()); + PHYSFS_close(f); + return(1); + } /* if */ + } /* if */ + + bw = strlen(WRITESTR); + rc = PHYSFS_write(f, WRITESTR, 1, bw); if (rc != bw) { printf("Wrote (%d) of (%d) bytes. Reason: [%s].\n", @@ -540,8 +790,22 @@ static int cmd_write(char *args) printf("failed to open. Reason: [%s].\n", PHYSFS_getLastError()); else { - size_t bw = strlen(WRITESTR); - PHYSFS_sint64 rc = PHYSFS_write(f, WRITESTR, 1, bw); + size_t bw; + PHYSFS_sint64 rc; + + if (do_buffer_size) + { + if (!PHYSFS_setBuffer(f, do_buffer_size)) + { + printf("failed to set file buffer. Reason: [%s].\n", + PHYSFS_getLastError()); + PHYSFS_close(f); + return(1); + } /* if */ + } /* if */ + + bw = strlen(WRITESTR); + rc = PHYSFS_write(f, WRITESTR, 1, bw); if (rc != bw) { printf("Wrote (%d) of (%d) bytes. Reason: [%s].\n", @@ -648,6 +912,8 @@ static const command_info commands[] = { "append", cmd_append, 1, "" }, { "write", cmd_write, 1, "" }, { "getlastmodtime", cmd_getlastmodtime, 1, "" }, + { "setbuffer", cmd_setbuffer, 1, "" }, + { "stressbuffer", cmd_stressbuffer, 1, "" }, { NULL, NULL, -1, NULL } };