Simplification of uio Stream functions. No more internal seeks.
Taking advantage of the POSIX/SUSv2/C requirement that input and output operations may not immediately follow eachother. git-svn-id: svn://svn.code.sf.net/p/sc2/code/trunk@2631 8092fc87-c524-0410-9efc-e669fe64eaf9
This commit is contained in:
+1
-1
@@ -1,5 +1,5 @@
|
||||
Changes towards version 0.7:
|
||||
- None yet
|
||||
- Simplification of uio Stream functions. No more internal seeks. - SvdB
|
||||
|
||||
Changes towards version 0.6:
|
||||
- Fixed a bug where an input delay was used for non-network games - SvdB
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
Needed before use in UQM:
|
||||
Needed for use in UQM:
|
||||
- documentation
|
||||
- configuring for GLOB
|
||||
- when doing uio_getStdioPhysical(), if write access is required, but
|
||||
@@ -16,7 +16,6 @@ Documentation:
|
||||
the physical structure is kept up to date when an entry is
|
||||
removed or added.
|
||||
- stream stuff is not thread safe
|
||||
writing using stream functions is not implemented.
|
||||
- uio_fflush() does not accept NULL as argument to flush all streams,
|
||||
like stdio fflush() does.
|
||||
- uio_close() does never fail
|
||||
@@ -35,7 +34,6 @@ Documentation:
|
||||
- You can use mount stuff from other repositories.
|
||||
|
||||
Bugs:
|
||||
- uio_Stream file might get unaligned (not sure).
|
||||
- 'openDir(repository, "dir/")' will have the trailing '/'
|
||||
in the dirHandle, which will cause problems.
|
||||
- 'openDirRelative(repository, "/")' causes segfaults later on
|
||||
@@ -53,18 +51,8 @@ Bugs:
|
||||
issue. SIZE_MAX can be used to check for sizeof(size_t) at runtime.
|
||||
- remove() is probably more compatible than unlink(). remove() is part
|
||||
of the C standard (as well as POSIX), unlink() is just POSIX.
|
||||
- seeking in files opened as "text" on Windows goes wrong. As uiostream
|
||||
seeks itself, this can happen when there are no explicit seeks
|
||||
by the user of uio. Are the seeks really necessary anyhow?
|
||||
- seeking in files opened as "text" on Windows goes wrong.
|
||||
- Network paths on Windows are not accepted.
|
||||
- From the C standard: "[...] output shall not be directly followed by input
|
||||
without an intervening call to the fflush function or to a file
|
||||
positioning function (fseek, fsetpos, or rewind), and input shall not be
|
||||
directly followed by output without an intervening call to a file
|
||||
positioning function, unless the input operation encounters end-of- file."
|
||||
This means some code in uiostream can be simplified, and a whole lot of
|
||||
seeks will never be necessary. (It will not even be nessessary to keep
|
||||
track of the current location in the file).
|
||||
- No CRLF translation (and ^Z recognition) is done for files read from
|
||||
zip files, even though that may be expected on Windows.
|
||||
|
||||
|
||||
@@ -31,17 +31,21 @@
|
||||
#endif
|
||||
|
||||
#define uio_Stream_BLOCK_SIZE 1024
|
||||
// The alignment of file reads as well as the buffer size.
|
||||
#define uio_Stream_BLOCK_PAGE_START(offset) \
|
||||
((offset) & ~(uio_Stream_BLOCK_SIZE - 1))
|
||||
|
||||
static inline uio_Stream *uio_Stream_new(uio_Handle *handle, int openFlags);
|
||||
static inline void uio_Stream_delete(uio_Stream *stream);
|
||||
static inline uio_Stream *uio_Stream_alloc(void);
|
||||
static inline void uio_Stream_free(uio_Stream *stream);
|
||||
#ifdef NDEBUG
|
||||
# define uio_assertReadSanity(stream)
|
||||
# define uio_assertWriteSanity(stream)
|
||||
#else
|
||||
static void uio_assertReadSanity(uio_Stream *stream);
|
||||
static void uio_assertWriteSanity(uio_Stream *stream);
|
||||
#endif
|
||||
static int uio_Stream_fillReadBuffer(uio_Stream *stream);
|
||||
static int uio_Stream_alignReadBuffer(uio_Stream *stream);
|
||||
static int uio_Stream_flushWriteBuffer(uio_Stream *stream);
|
||||
static void uio_Stream_discardReadBuffer(uio_Stream *stream);
|
||||
|
||||
|
||||
uio_Stream *
|
||||
@@ -108,18 +112,18 @@ uio_fopen(uio_DirHandle *dir, const char *path, const char *mode) {
|
||||
|
||||
int
|
||||
uio_fclose(uio_Stream *stream) {
|
||||
if (stream->writeStart != NULL)
|
||||
if (stream->operation == uio_StreamOperation_write)
|
||||
uio_Stream_flushWriteBuffer(stream);
|
||||
uio_close(stream->handle);
|
||||
uio_Stream_delete(stream);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// If less than nmembs could be read, or an error occurs, the file pointer
|
||||
// is undefined. clearerr() followed by fseek() need to be called before
|
||||
// attempting to read or write again.
|
||||
// I don't have the C standard myself, but I suspect this is the
|
||||
// official behaviour for fread() and fwrite().
|
||||
// "The file position indicator for the stream (if defined) is advanced by
|
||||
// the number of characters successfully read. If an error occurs, the
|
||||
// resulting value of the file position indicator for the stream is
|
||||
// indeterminate. If a partial element is read, its value is
|
||||
// indeterminate." (from POSIX for fread()).
|
||||
size_t
|
||||
uio_fread(void *buf, size_t size, size_t nmemb, uio_Stream *stream) {
|
||||
size_t bytesToRead;
|
||||
@@ -128,21 +132,17 @@ uio_fread(void *buf, size_t size, size_t nmemb, uio_Stream *stream) {
|
||||
bytesToRead = size * nmemb;
|
||||
bytesRead = 0;
|
||||
|
||||
assert((stream->openFlags & O_ACCMODE) != O_WRONLY);
|
||||
if (stream->writeStart != NULL) {
|
||||
if (uio_Stream_flushWriteBuffer(stream) == -1) {
|
||||
// errno is set
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
if (stream->readEnd - stream->bufPtr > 0) {
|
||||
uio_assertReadSanity(stream);
|
||||
stream->operation = uio_StreamOperation_read;
|
||||
|
||||
if (stream->dataEnd > stream->dataStart) {
|
||||
// First use what's in the buffer.
|
||||
size_t numRead;
|
||||
|
||||
numRead = minu(stream->readEnd - stream->bufPtr, bytesToRead);
|
||||
memcpy(buf, stream->bufPtr, numRead);
|
||||
numRead = minu(stream->dataEnd - stream->dataStart, bytesToRead);
|
||||
memcpy(buf, stream->dataStart, numRead);
|
||||
buf = (void *) ((char *) buf + numRead);
|
||||
stream->bufPtr += numRead;
|
||||
stream->dataStart += numRead;
|
||||
bytesToRead -= numRead;
|
||||
bytesRead += numRead;
|
||||
}
|
||||
@@ -152,6 +152,7 @@ uio_fread(void *buf, size_t size, size_t nmemb, uio_Stream *stream) {
|
||||
}
|
||||
|
||||
{
|
||||
// Read the rest directly into the caller's buffer.
|
||||
ssize_t numRead;
|
||||
numRead = uio_read(stream->handle, buf, bytesToRead);
|
||||
if (numRead == -1) {
|
||||
@@ -159,13 +160,12 @@ uio_fread(void *buf, size_t size, size_t nmemb, uio_Stream *stream) {
|
||||
goto out;
|
||||
}
|
||||
bytesRead += numRead;
|
||||
stream->seekLow += numRead;
|
||||
if ((size_t) numRead < bytesToRead) {
|
||||
// End of file
|
||||
stream->status = uio_Stream_STATUS_EOF;
|
||||
stream->operation = uio_StreamOperation_none;
|
||||
goto out;
|
||||
}
|
||||
uio_Stream_alignReadBuffer(stream);
|
||||
}
|
||||
|
||||
out:
|
||||
@@ -179,14 +179,8 @@ uio_fgets(char *s, int size, uio_Stream *stream) {
|
||||
int orgSize;
|
||||
char *buf;
|
||||
|
||||
assert((stream->openFlags & O_ACCMODE) != O_WRONLY);
|
||||
|
||||
if (stream->writeStart != NULL) {
|
||||
if (uio_Stream_flushWriteBuffer(stream) == -1) {
|
||||
// errno is set
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
uio_assertReadSanity(stream);
|
||||
stream->operation = uio_StreamOperation_read;
|
||||
|
||||
size--;
|
||||
orgSize = size;
|
||||
@@ -196,15 +190,16 @@ uio_fgets(char *s, int size, uio_Stream *stream) {
|
||||
const char *newLinePos;
|
||||
|
||||
// Fill buffer if empty.
|
||||
if (stream->bufPtr >= stream->readEnd) {
|
||||
if (stream->dataStart == stream->dataEnd) {
|
||||
if (uio_Stream_fillReadBuffer(stream) == -1) {
|
||||
// errno is set
|
||||
stream->status = uio_Stream_STATUS_ERROR;
|
||||
return NULL;
|
||||
}
|
||||
if (stream->bufPtr == stream->readEnd) {
|
||||
if (stream->dataStart == stream->dataEnd) {
|
||||
// End-of-file
|
||||
stream->status = uio_Stream_STATUS_EOF;
|
||||
stream->operation = uio_StreamOperation_none;
|
||||
if (size == orgSize) {
|
||||
// Nothing was read.
|
||||
return NULL;
|
||||
@@ -212,22 +207,21 @@ uio_fgets(char *s, int size, uio_Stream *stream) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
assert (stream->bufPtr < stream->readEnd);
|
||||
|
||||
// Search in buffer
|
||||
maxRead = minu(stream->readEnd - stream->bufPtr, size);
|
||||
newLinePos = memchr(stream->bufPtr, '\n', maxRead);
|
||||
maxRead = minu(stream->dataEnd - stream->dataStart, size);
|
||||
newLinePos = memchr(stream->dataStart, '\n', maxRead);
|
||||
if (newLinePos != NULL) {
|
||||
// Newline found.
|
||||
maxRead = newLinePos + 1 - stream->bufPtr;
|
||||
memcpy(buf, stream->bufPtr, maxRead);
|
||||
stream->bufPtr += maxRead;
|
||||
maxRead = newLinePos + 1 - stream->dataStart;
|
||||
memcpy(buf, stream->dataStart, maxRead);
|
||||
stream->dataStart += maxRead;
|
||||
buf[maxRead] = '\0';
|
||||
return buf;
|
||||
}
|
||||
// No newline present.
|
||||
memcpy(buf, stream->bufPtr, maxRead);
|
||||
stream->bufPtr += maxRead;
|
||||
memcpy(buf, stream->dataStart, maxRead);
|
||||
stream->dataStart += maxRead;
|
||||
buf += maxRead;
|
||||
size -= maxRead;
|
||||
}
|
||||
@@ -240,30 +234,25 @@ int
|
||||
uio_fgetc(uio_Stream *stream) {
|
||||
int result;
|
||||
|
||||
assert((stream->openFlags & O_ACCMODE) != O_WRONLY);
|
||||
uio_assertReadSanity(stream);
|
||||
stream->operation = uio_StreamOperation_read;
|
||||
|
||||
if (stream->writeStart != NULL) {
|
||||
if (uio_Stream_flushWriteBuffer(stream) == -1) {
|
||||
// errno is set
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (stream->bufPtr >= stream->readEnd) {
|
||||
if (stream->dataStart == stream->dataEnd) {
|
||||
// Buffer is empty
|
||||
if (uio_Stream_fillReadBuffer(stream) == -1) {
|
||||
stream->status = uio_Stream_STATUS_ERROR;
|
||||
return (int) EOF;
|
||||
}
|
||||
if (stream->bufPtr == stream->readEnd) {
|
||||
if (stream->dataStart == stream->dataEnd) {
|
||||
// End-of-file
|
||||
stream->status = uio_Stream_STATUS_EOF;
|
||||
stream->operation = uio_StreamOperation_none;
|
||||
return (int) EOF;
|
||||
}
|
||||
}
|
||||
assert(stream->bufPtr < stream->readEnd);
|
||||
|
||||
result = (int) *((unsigned char *) stream->bufPtr);
|
||||
stream->bufPtr++;
|
||||
result = (int) *((unsigned char *) stream->dataStart);
|
||||
stream->dataStart++;
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -283,18 +272,20 @@ uio_fputc(int c, uio_Stream *stream) {
|
||||
assert((stream->openFlags & O_ACCMODE) != O_RDONLY);
|
||||
assert(c >= 0 && c <= 255);
|
||||
|
||||
if (stream->writeStart == NULL)
|
||||
stream->writeStart = stream->bufPtr;
|
||||
if (stream->bufPtr == stream->bufEnd) {
|
||||
uio_assertWriteSanity(stream);
|
||||
stream->operation = uio_StreamOperation_write;
|
||||
|
||||
if (stream->dataEnd == stream->bufEnd) {
|
||||
// The buffer is full. Flush it out.
|
||||
if (uio_Stream_flushWriteBuffer(stream) == -1) {
|
||||
// errno is set
|
||||
// Error status (for ferror()) is set.
|
||||
return EOF;
|
||||
}
|
||||
}
|
||||
|
||||
*(unsigned char *) stream->bufPtr = (unsigned char) c;
|
||||
stream->bufPtr++;
|
||||
*(unsigned char *) stream->dataEnd = (unsigned char) c;
|
||||
stream->dataEnd++;
|
||||
return c;
|
||||
}
|
||||
|
||||
@@ -312,78 +303,46 @@ int
|
||||
uio_fseek(uio_Stream *stream, long offset, int whence) {
|
||||
int newPos;
|
||||
|
||||
if (stream->writeStart != NULL) {
|
||||
if (stream->operation == uio_StreamOperation_read) {
|
||||
uio_Stream_discardReadBuffer(stream);
|
||||
} else if (stream->operation == uio_StreamOperation_write) {
|
||||
if (uio_Stream_flushWriteBuffer(stream) == -1) {
|
||||
// errno is set
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
assert(stream->dataStart == stream->buf);
|
||||
assert(stream->dataEnd == stream->buf);
|
||||
stream->operation = uio_StreamOperation_none;
|
||||
|
||||
assert(whence == SEEK_SET || whence == SEEK_CUR || whence == SEEK_END);
|
||||
switch(whence) {
|
||||
case SEEK_SET:
|
||||
break;
|
||||
case SEEK_CUR:
|
||||
offset += stream->seekLow - (stream->readEnd - stream->bufPtr);
|
||||
break;
|
||||
case SEEK_END: {
|
||||
struct stat statBuf;
|
||||
|
||||
if (uio_fstat(stream->handle, &statBuf) == -1) {
|
||||
// errno is set
|
||||
return -1;
|
||||
}
|
||||
offset += statBuf.st_size;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: when implementing pushback: throw away pushback buffer.
|
||||
|
||||
// Maybe the new location is still inside the read buffer.
|
||||
// If not, we must throw the buffer away.
|
||||
// If the buffer was polluted from calls to ungetc(),
|
||||
// always throw away the buffer.
|
||||
if (offset >= stream->seekLow - (stream->readEnd - stream->buf) &&
|
||||
offset <= stream->seekLow) {
|
||||
// The buffer can be reused.
|
||||
stream->status = uio_Stream_STATUS_OK;
|
||||
stream->bufPtr = stream->buf +
|
||||
(stream->readEnd - stream->buf) +
|
||||
(offset - stream->seekLow);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// The read buffer is not reusable.
|
||||
newPos = uio_lseek(stream->handle, offset, SEEK_SET);
|
||||
newPos = uio_lseek(stream->handle, offset, whence);
|
||||
if (newPos == -1) {
|
||||
// errno is set
|
||||
return -1;
|
||||
}
|
||||
stream->seekLow = newPos;
|
||||
stream->status = uio_Stream_STATUS_OK;
|
||||
// Clear error or end-of-file flag.
|
||||
stream->bufPtr = stream->buf;
|
||||
stream->readEnd = stream->buf;
|
||||
|
||||
if ((stream->openFlags & O_ACCMODE) == O_RDONLY) {
|
||||
// We're done if there's no writing involved.
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (uio_Stream_alignReadBuffer(stream) == -1) {
|
||||
// Even when the buffer can't be filled, it might be
|
||||
// a suitable location for writing (for instance, at the end
|
||||
// of the file).
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
long
|
||||
uio_ftell(uio_Stream *stream) {
|
||||
return (long) stream->seekLow - (stream->readEnd - stream->bufPtr);
|
||||
off_t newPos;
|
||||
|
||||
newPos = uio_lseek(stream->handle, 0, SEEK_CUR);
|
||||
if (newPos == (off_t) -1) {
|
||||
// errno is set
|
||||
return (long) -1;
|
||||
}
|
||||
|
||||
if (stream->operation == uio_StreamOperation_write) {
|
||||
newPos += stream->dataEnd - stream->dataStart;
|
||||
} else if (stream->operation == uio_StreamOperation_read) {
|
||||
newPos -= stream->dataEnd - stream->dataStart;
|
||||
}
|
||||
|
||||
return (long) newPos;
|
||||
}
|
||||
|
||||
// If less that nmemb elements could be written, or an error occurs, the
|
||||
@@ -396,69 +355,45 @@ uio_fwrite(const void *buf, size_t size, size_t nmemb, uio_Stream *stream) {
|
||||
ssize_t bytesToWrite;
|
||||
ssize_t bytesWritten;
|
||||
|
||||
assert((stream->openFlags & O_ACCMODE) != O_RDONLY);
|
||||
uio_assertWriteSanity(stream);
|
||||
|
||||
// NB. If a file is opened in append mode, the file position indicator
|
||||
// is moved to the end of the file before writing.
|
||||
// We leave that up to the physical layer.
|
||||
|
||||
bytesToWrite = size * nmemb;
|
||||
if (stream->writeStart == NULL)
|
||||
stream->writeStart = stream->bufPtr;
|
||||
if (bytesToWrite < stream->bufEnd - stream->bufPtr) {
|
||||
if (bytesToWrite < stream->bufEnd - stream->dataEnd) {
|
||||
// There's enough space in the write buffer to store everything.
|
||||
memcpy(stream->bufPtr, buf, bytesToWrite);
|
||||
stream->bufPtr += bytesToWrite;
|
||||
memcpy(stream->dataEnd, buf, bytesToWrite);
|
||||
stream->dataEnd += bytesToWrite;
|
||||
return nmemb;
|
||||
}
|
||||
|
||||
// Not enough space in the write buffer to write everything.
|
||||
// Flush what's left in the write buffer, and then directly write 'buf'.
|
||||
// Flush what's left in the write buffer first.
|
||||
if (uio_Stream_flushWriteBuffer(stream) == -1) {
|
||||
// errno is set
|
||||
// Error status (for ferror()) is set.
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (stream->openFlags & O_APPEND) {
|
||||
// If a file is opened in append mode, the file position indicator
|
||||
// is moved to the end of the file before writing.
|
||||
// We leave that up to the physical layer.
|
||||
} else {
|
||||
// TODO: It's possible that uio_Stream_flushWriteBuffer() just
|
||||
// seeked back from the end of the part written, to the end of
|
||||
// the part read. This seek will just undo that effect, so
|
||||
// there's room for optimisation here. (decouple
|
||||
// stream->seekLow from stream->readEnd by adding a seekHigh
|
||||
// var?)
|
||||
if (uio_lseek(stream->handle, stream->seekLow -
|
||||
(stream->readEnd - stream->bufPtr), SEEK_SET) == -1) {
|
||||
// errno is set
|
||||
return 0;
|
||||
}
|
||||
stream->seekLow -= (stream->readEnd - stream->bufPtr);
|
||||
if (bytesToWrite < stream->bufEnd - stream->dataEnd) {
|
||||
// The now empty write buffer is large enough to store everything.
|
||||
memcpy(stream->dataEnd, buf, bytesToWrite);
|
||||
stream->dataEnd += bytesToWrite;
|
||||
return nmemb;
|
||||
}
|
||||
|
||||
// There is more data to write than fits in the (empty) write buffer.
|
||||
// The data is written directly, in its entirety, without going
|
||||
// through the write buffer.
|
||||
bytesWritten = uio_write(stream->handle, buf, bytesToWrite);
|
||||
if (bytesWritten != bytesToWrite) {
|
||||
stream->status = uio_Stream_STATUS_ERROR;
|
||||
if (bytesWritten == -1)
|
||||
return 0;
|
||||
}
|
||||
if (stream->openFlags & O_APPEND) {
|
||||
// Determine the new location in the file.
|
||||
off_t newPos = uio_lseek(stream->handle, 0, SEEK_CUR);
|
||||
if (newPos == -1) {
|
||||
// errno is set
|
||||
stream->status = uio_Stream_STATUS_ERROR;
|
||||
return 0;
|
||||
// XXX: is returning 0 the best thing to do? The data
|
||||
// has actually been successfully written.
|
||||
}
|
||||
stream->seekLow = newPos;
|
||||
} else {
|
||||
stream->seekLow += bytesWritten;
|
||||
}
|
||||
// TODO: readStart is no longer aligned on a block.
|
||||
stream->readStart = stream->buf;
|
||||
stream->bufPtr = stream->buf;
|
||||
stream->readEnd = stream->buf;
|
||||
stream->writeStart = NULL;
|
||||
|
||||
if (bytesWritten == bytesToWrite)
|
||||
return nmemb;
|
||||
return (size_t) bytesWritten / size;
|
||||
@@ -469,8 +404,16 @@ uio_fwrite(const void *buf, size_t size, size_t nmemb, uio_Stream *stream) {
|
||||
int
|
||||
uio_fflush(uio_Stream *stream) {
|
||||
assert(stream != NULL);
|
||||
assert((stream->openFlags & O_ACCMODE) != O_RDONLY);
|
||||
return uio_Stream_flushWriteBuffer(stream);
|
||||
|
||||
if (stream->operation == uio_StreamOperation_write) {
|
||||
if (uio_Stream_flushWriteBuffer(stream) == -1) {
|
||||
// errno is set
|
||||
return (int) EOF;
|
||||
}
|
||||
stream->operation = uio_StreamOperation_none;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
@@ -494,85 +437,85 @@ uio_streamHandle(uio_Stream *stream) {
|
||||
return stream->handle;
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
static void
|
||||
uio_assertReadSanity(uio_Stream *stream) {
|
||||
assert((stream->openFlags & O_ACCMODE) != O_WRONLY);
|
||||
|
||||
if (stream->operation == uio_StreamOperation_write) {
|
||||
// "[...] output shall not be directly followed by input without an
|
||||
// intervening call to the fflush function or to a file positioning
|
||||
// function (fseek, fsetpos, or rewind), and input shall not be
|
||||
// directly followed by output without an intervening call to a file
|
||||
// positioning function, unless the input operation encounters
|
||||
// end-of-file." (POSIX, C)
|
||||
fprintf(stderr, "Error: Reading on a file directly after writing, "
|
||||
"without an intervening call to fflush() or a file "
|
||||
"positioning function.\n");
|
||||
abort();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef NDEBUG
|
||||
static void
|
||||
uio_assertWriteSanity(uio_Stream *stream) {
|
||||
assert((stream->openFlags & O_ACCMODE) != O_RDONLY);
|
||||
|
||||
if (stream->operation == uio_StreamOperation_read) {
|
||||
// "[...] output shall not be directly followed by input without an
|
||||
// intervening call to the fflush function or to a file positioning
|
||||
// function (fseek, fsetpos, or rewind), and input shall not be
|
||||
// directly followed by output without an intervening call to a file
|
||||
// positioning function, unless the input operation encounters
|
||||
// end-of-file." (POSIX, C)
|
||||
fprintf(stderr, "Error: Writing on a file directly after reading, "
|
||||
"without an intervening call to a file positioning "
|
||||
"function.\n");
|
||||
abort();
|
||||
}
|
||||
assert(stream->dataStart == stream->buf);
|
||||
}
|
||||
#endif
|
||||
|
||||
static int
|
||||
uio_Stream_flushWriteBuffer(uio_Stream *stream) {
|
||||
ssize_t bytesWritten;
|
||||
off_t newPos;
|
||||
|
||||
newPos = uio_lseek(stream->handle,
|
||||
(off_t) (stream->seekLow - (stream->readEnd - stream->writeStart)),
|
||||
SEEK_SET);
|
||||
if (newPos == -1) {
|
||||
// errno is set
|
||||
return -1;
|
||||
}
|
||||
stream->seekLow = newPos;
|
||||
assert(stream->operation == uio_StreamOperation_write);
|
||||
|
||||
bytesWritten = uio_write(stream->handle, stream->writeStart,
|
||||
stream->bufPtr - stream->writeStart);
|
||||
if (bytesWritten != stream->bufPtr - stream->writeStart) {
|
||||
bytesWritten = uio_write(stream->handle, stream->dataStart,
|
||||
stream->dataEnd - stream->dataStart);
|
||||
if (bytesWritten != stream->dataEnd - stream->dataStart) {
|
||||
stream->status = uio_Stream_STATUS_ERROR;
|
||||
if (bytesWritten != -1)
|
||||
stream->seekLow += bytesWritten;
|
||||
return -1;
|
||||
}
|
||||
stream->seekLow += bytesWritten;
|
||||
stream->writeStart = NULL;
|
||||
assert(stream->dataStart == stream->buf);
|
||||
stream->dataEnd = stream->buf;
|
||||
|
||||
if (stream->bufPtr > stream->readEnd) {
|
||||
stream->readEnd = stream->bufPtr;
|
||||
} else {
|
||||
newPos = uio_lseek(stream->handle,
|
||||
(off_t) (stream->seekLow + (stream->readEnd - stream->bufPtr)),
|
||||
SEEK_SET);
|
||||
if (newPos == -1) {
|
||||
// errno is set
|
||||
// At least keep the internal state consistent so that
|
||||
// a new uio_fseek() (after an uio_clearerr()) can succeed:
|
||||
stream->readEnd = stream->bufPtr;
|
||||
// SeekLow of the buffer is not aligned on a block.
|
||||
return -1;
|
||||
}
|
||||
stream->seekLow = newPos;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
uio_Stream_discardReadBuffer(uio_Stream *stream) {
|
||||
assert(stream->operation == uio_StreamOperation_read);
|
||||
stream->dataStart = stream->buf;
|
||||
stream->dataEnd = stream->buf;
|
||||
// TODO: when implementing pushback: throw away pushback buffer.
|
||||
}
|
||||
|
||||
static int
|
||||
uio_Stream_fillReadBuffer(uio_Stream *stream) {
|
||||
ssize_t numRead;
|
||||
|
||||
assert(stream->bufPtr == stream->readEnd);
|
||||
assert(stream->operation == uio_StreamOperation_read);
|
||||
|
||||
numRead = uio_read(stream->handle, stream->buf,
|
||||
uio_Stream_BLOCK_SIZE);
|
||||
if (numRead == -1)
|
||||
return -1;
|
||||
stream->bufPtr = stream->buf;
|
||||
stream->readEnd = stream->buf + numRead;
|
||||
stream->seekLow += numRead;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
uio_Stream_alignReadBuffer(uio_Stream *stream) {
|
||||
off_t endAlign;
|
||||
ssize_t numRead;
|
||||
|
||||
endAlign = uio_Stream_BLOCK_PAGE_START(stream->seekLow +
|
||||
uio_Stream_BLOCK_SIZE - 1);
|
||||
if (endAlign == stream->seekLow) {
|
||||
// Nothing to do.
|
||||
return 0;
|
||||
}
|
||||
|
||||
numRead = uio_read(stream->handle, stream->buf,
|
||||
endAlign - stream->seekLow);
|
||||
if (numRead == -1)
|
||||
return -1;
|
||||
stream->bufPtr = stream->buf;
|
||||
stream->readEnd = stream->buf + numRead;
|
||||
stream->seekLow += numRead;
|
||||
stream->dataStart = stream->buf;
|
||||
stream->dataEnd = stream->buf + numRead;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -584,13 +527,11 @@ uio_Stream_new(uio_Handle *handle, int openFlags) {
|
||||
result->handle = handle;
|
||||
result->openFlags = openFlags;
|
||||
result->status = uio_Stream_STATUS_OK;
|
||||
result->operation = uio_StreamOperation_none;
|
||||
result->buf = uio_malloc(uio_Stream_BLOCK_SIZE);
|
||||
result->bufPtr = result->buf;
|
||||
result->readStart = result->buf;
|
||||
result->readEnd = result->buf;
|
||||
result->writeStart = NULL;
|
||||
result->dataStart = result->buf;
|
||||
result->dataEnd = result->buf;
|
||||
result->bufEnd = result->buf + uio_Stream_BLOCK_SIZE;
|
||||
result->seekLow = 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@@ -55,48 +55,32 @@ uio_Handle *uio_streamHandle(uio_Stream *stream);
|
||||
#include <fcntl.h>
|
||||
#include "iointrn.h"
|
||||
|
||||
/*
|
||||
* Layout of buf:
|
||||
*
|
||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
* | | | | | | | |D|D|D|D|D|D|D|D| | | | | | | |X|X|X|X|X|X|X|X|X|X|X|X|X|X|
|
||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
* ^ ^ ^ ^ ^
|
||||
* +-readStart +-writeStart +-bufPtr +-readEnd bufEnd-+
|
||||
*
|
||||
* buf - start of the buffer
|
||||
* readStart - start of the segment that was read
|
||||
* writeStart - start of the part which is written, or NULL if nothing
|
||||
* written.
|
||||
* bufPtr - file pointer; here new reads or writes are done.
|
||||
* readEnd - end of the part of the file that is pre-read.
|
||||
* bufEnd - end of memory allocated for the buffer.
|
||||
* D - dirty, needs to be written to disk
|
||||
* X - invalid; reserved but not used memory
|
||||
*
|
||||
* Invariants:
|
||||
* buf <= bufPtr <= bufEnd
|
||||
* If writeStart != NULL: buf <= writeStart <= bufPtr <= bufEnd
|
||||
* buf <= readStart <= readEnd <= bufEnd
|
||||
* Note that writeStart and bufPtr may point past readEnd.
|
||||
*
|
||||
*/
|
||||
typedef enum {
|
||||
uio_StreamOperation_none,
|
||||
uio_StreamOperation_read,
|
||||
uio_StreamOperation_write
|
||||
} uio_StreamOperation;
|
||||
|
||||
struct uio_Stream {
|
||||
char *buf;
|
||||
char *bufPtr;
|
||||
char *readStart;
|
||||
// Not really used atm.
|
||||
char *writeStart;
|
||||
char *readEnd;
|
||||
// Start of the buffer.
|
||||
char *dataStart;
|
||||
// Start of the part of the buffer that is in use.
|
||||
char *dataEnd;
|
||||
// Start of the unused part of the buffer.
|
||||
char *bufEnd;
|
||||
off_t seekLow;
|
||||
// Low(er) level file pointer. Always points to the position just
|
||||
// past the part that's in the buffer as [readStart..readEnd].
|
||||
// End of the buffer.
|
||||
// INV: buf <= dataStart <= dataEnd <= bufEnd
|
||||
// INV: if 'operation == uio_StreamOperation_write' then buf == dataStart
|
||||
|
||||
uio_Handle *handle;
|
||||
int status;
|
||||
#define uio_Stream_STATUS_OK 0
|
||||
#define uio_Stream_STATUS_EOF 1
|
||||
#define uio_Stream_STATUS_ERROR 2
|
||||
uio_StreamOperation operation;
|
||||
// What was the last action (reading or writing). This
|
||||
// determines whether the buffer is a read or write buffer.
|
||||
int openFlags;
|
||||
// Flags used for opening the file.
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user