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:
Meep-Eep
2006-12-20 20:15:43 +00:00
parent 9cb412b41f
commit 97626d2ab5
4 changed files with 187 additions and 274 deletions
+1 -1
View File
@@ -1,5 +1,5 @@
Changes towards version 0.7: Changes towards version 0.7:
- None yet - Simplification of uio Stream functions. No more internal seeks. - SvdB
Changes towards version 0.6: Changes towards version 0.6:
- Fixed a bug where an input delay was used for non-network games - SvdB - Fixed a bug where an input delay was used for non-network games - SvdB
+3 -15
View File
@@ -1,4 +1,4 @@
Needed before use in UQM: Needed for use in UQM:
- documentation - documentation
- configuring for GLOB - configuring for GLOB
- when doing uio_getStdioPhysical(), if write access is required, but - 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 the physical structure is kept up to date when an entry is
removed or added. removed or added.
- stream stuff is not thread safe - 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, - uio_fflush() does not accept NULL as argument to flush all streams,
like stdio fflush() does. like stdio fflush() does.
- uio_close() does never fail - uio_close() does never fail
@@ -35,10 +34,9 @@ Documentation:
- You can use mount stuff from other repositories. - You can use mount stuff from other repositories.
Bugs: Bugs:
- uio_Stream file might get unaligned (not sure).
- 'openDir(repository, "dir/")' will have the trailing '/' - 'openDir(repository, "dir/")' will have the trailing '/'
in the dirHandle, which will cause problems. in the dirHandle, which will cause problems.
- 'openDirRelative(repository, "/")' causes segfaults lateron - 'openDirRelative(repository, "/")' causes segfaults later on
- uio_rename() doesn't work on directories - uio_rename() doesn't work on directories
uio_getPhysicalAccess() needs to be changed so that it works the same on uio_getPhysicalAccess() needs to be changed so that it works the same on
dirs as on files. stat() can be cleaned up too then. dirs as on files. stat() can be cleaned up too then.
@@ -53,18 +51,8 @@ Bugs:
issue. SIZE_MAX can be used to check for sizeof(size_t) at runtime. issue. SIZE_MAX can be used to check for sizeof(size_t) at runtime.
- remove() is probably more compatible than unlink(). remove() is part - remove() is probably more compatible than unlink(). remove() is part
of the C standard (as well as POSIX), unlink() is just POSIX. of the C standard (as well as POSIX), unlink() is just POSIX.
- seeking in files opened as "text" on Windows goes wrong. As uiostream - seeking in files opened as "text" on Windows goes wrong.
seeks itself, this can happen when there are no explicit seeks
by the user of uio. Are the seeks really necessary anyhow?
- Network paths on Windows are not accepted. - 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 - No CRLF translation (and ^Z recognition) is done for files read from
zip files, even though that may be expected on Windows. zip files, even though that may be expected on Windows.
+162 -221
View File
@@ -31,17 +31,21 @@
#endif #endif
#define uio_Stream_BLOCK_SIZE 1024 #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 uio_Stream *uio_Stream_new(uio_Handle *handle, int openFlags);
static inline void uio_Stream_delete(uio_Stream *stream); static inline void uio_Stream_delete(uio_Stream *stream);
static inline uio_Stream *uio_Stream_alloc(void); static inline uio_Stream *uio_Stream_alloc(void);
static inline void uio_Stream_free(uio_Stream *stream); 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_fillReadBuffer(uio_Stream *stream);
static int uio_Stream_alignReadBuffer(uio_Stream *stream);
static int uio_Stream_flushWriteBuffer(uio_Stream *stream); static int uio_Stream_flushWriteBuffer(uio_Stream *stream);
static void uio_Stream_discardReadBuffer(uio_Stream *stream);
uio_Stream * uio_Stream *
@@ -108,18 +112,18 @@ uio_fopen(uio_DirHandle *dir, const char *path, const char *mode) {
int int
uio_fclose(uio_Stream *stream) { uio_fclose(uio_Stream *stream) {
if (stream->writeStart != NULL) if (stream->operation == uio_StreamOperation_write)
uio_Stream_flushWriteBuffer(stream); uio_Stream_flushWriteBuffer(stream);
uio_close(stream->handle); uio_close(stream->handle);
uio_Stream_delete(stream); uio_Stream_delete(stream);
return 0; return 0;
} }
// If less than nmembs could be read, or an error occurs, the file pointer // "The file position indicator for the stream (if defined) is advanced by
// is undefined. clearerr() followed by fseek() need to be called before // the number of characters successfully read. If an error occurs, the
// attempting to read or write again. // resulting value of the file position indicator for the stream is
// I don't have the C standard myself, but I suspect this is the // indeterminate. If a partial element is read, its value is
// official behaviour for fread() and fwrite(). // indeterminate." (from POSIX for fread()).
size_t size_t
uio_fread(void *buf, size_t size, size_t nmemb, uio_Stream *stream) { uio_fread(void *buf, size_t size, size_t nmemb, uio_Stream *stream) {
size_t bytesToRead; size_t bytesToRead;
@@ -128,21 +132,17 @@ uio_fread(void *buf, size_t size, size_t nmemb, uio_Stream *stream) {
bytesToRead = size * nmemb; bytesToRead = size * nmemb;
bytesRead = 0; bytesRead = 0;
assert((stream->openFlags & O_ACCMODE) != O_WRONLY); uio_assertReadSanity(stream);
if (stream->writeStart != NULL) { stream->operation = uio_StreamOperation_read;
if (uio_Stream_flushWriteBuffer(stream) == -1) {
// errno is set if (stream->dataEnd > stream->dataStart) {
return -1;
}
}
if (stream->readEnd - stream->bufPtr > 0) {
// First use what's in the buffer. // First use what's in the buffer.
size_t numRead; size_t numRead;
numRead = minu(stream->readEnd - stream->bufPtr, bytesToRead); numRead = minu(stream->dataEnd - stream->dataStart, bytesToRead);
memcpy(buf, stream->bufPtr, numRead); memcpy(buf, stream->dataStart, numRead);
buf = (void *) ((char *) buf + numRead); buf = (void *) ((char *) buf + numRead);
stream->bufPtr += numRead; stream->dataStart += numRead;
bytesToRead -= numRead; bytesToRead -= numRead;
bytesRead += 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; ssize_t numRead;
numRead = uio_read(stream->handle, buf, bytesToRead); numRead = uio_read(stream->handle, buf, bytesToRead);
if (numRead == -1) { if (numRead == -1) {
@@ -159,13 +160,12 @@ uio_fread(void *buf, size_t size, size_t nmemb, uio_Stream *stream) {
goto out; goto out;
} }
bytesRead += numRead; bytesRead += numRead;
stream->seekLow += numRead;
if ((size_t) numRead < bytesToRead) { if ((size_t) numRead < bytesToRead) {
// End of file // End of file
stream->status = uio_Stream_STATUS_EOF; stream->status = uio_Stream_STATUS_EOF;
stream->operation = uio_StreamOperation_none;
goto out; goto out;
} }
uio_Stream_alignReadBuffer(stream);
} }
out: out:
@@ -179,14 +179,8 @@ uio_fgets(char *s, int size, uio_Stream *stream) {
int orgSize; int orgSize;
char *buf; char *buf;
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 NULL;
}
}
size--; size--;
orgSize = size; orgSize = size;
@@ -196,15 +190,16 @@ uio_fgets(char *s, int size, uio_Stream *stream) {
const char *newLinePos; const char *newLinePos;
// Fill buffer if empty. // Fill buffer if empty.
if (stream->bufPtr >= stream->readEnd) { if (stream->dataStart == stream->dataEnd) {
if (uio_Stream_fillReadBuffer(stream) == -1) { if (uio_Stream_fillReadBuffer(stream) == -1) {
// errno is set // errno is set
stream->status = uio_Stream_STATUS_ERROR; stream->status = uio_Stream_STATUS_ERROR;
return NULL; return NULL;
} }
if (stream->bufPtr == stream->readEnd) { if (stream->dataStart == stream->dataEnd) {
// End-of-file // End-of-file
stream->status = uio_Stream_STATUS_EOF; stream->status = uio_Stream_STATUS_EOF;
stream->operation = uio_StreamOperation_none;
if (size == orgSize) { if (size == orgSize) {
// Nothing was read. // Nothing was read.
return NULL; return NULL;
@@ -212,22 +207,21 @@ uio_fgets(char *s, int size, uio_Stream *stream) {
break; break;
} }
} }
assert (stream->bufPtr < stream->readEnd);
// Search in buffer // Search in buffer
maxRead = minu(stream->readEnd - stream->bufPtr, size); maxRead = minu(stream->dataEnd - stream->dataStart, size);
newLinePos = memchr(stream->bufPtr, '\n', maxRead); newLinePos = memchr(stream->dataStart, '\n', maxRead);
if (newLinePos != NULL) { if (newLinePos != NULL) {
// Newline found. // Newline found.
maxRead = newLinePos + 1 - stream->bufPtr; maxRead = newLinePos + 1 - stream->dataStart;
memcpy(buf, stream->bufPtr, maxRead); memcpy(buf, stream->dataStart, maxRead);
stream->bufPtr += maxRead; stream->dataStart += maxRead;
buf[maxRead] = '\0'; buf[maxRead] = '\0';
return buf; return buf;
} }
// No newline present. // No newline present.
memcpy(buf, stream->bufPtr, maxRead); memcpy(buf, stream->dataStart, maxRead);
stream->bufPtr += maxRead; stream->dataStart += maxRead;
buf += maxRead; buf += maxRead;
size -= maxRead; size -= maxRead;
} }
@@ -240,30 +234,25 @@ int
uio_fgetc(uio_Stream *stream) { uio_fgetc(uio_Stream *stream) {
int result; int result;
assert((stream->openFlags & O_ACCMODE) != O_WRONLY); uio_assertReadSanity(stream);
stream->operation = uio_StreamOperation_read;
if (stream->writeStart != NULL) { if (stream->dataStart == stream->dataEnd) {
if (uio_Stream_flushWriteBuffer(stream) == -1) { // Buffer is empty
// errno is set
return -1;
}
}
if (stream->bufPtr >= stream->readEnd) {
if (uio_Stream_fillReadBuffer(stream) == -1) { if (uio_Stream_fillReadBuffer(stream) == -1) {
stream->status = uio_Stream_STATUS_ERROR; stream->status = uio_Stream_STATUS_ERROR;
return (int) EOF; return (int) EOF;
} }
if (stream->bufPtr == stream->readEnd) { if (stream->dataStart == stream->dataEnd) {
// End-of-file // End-of-file
stream->status = uio_Stream_STATUS_EOF; stream->status = uio_Stream_STATUS_EOF;
stream->operation = uio_StreamOperation_none;
return (int) EOF; return (int) EOF;
} }
} }
assert(stream->bufPtr < stream->readEnd);
result = (int) *((unsigned char *) stream->bufPtr); result = (int) *((unsigned char *) stream->dataStart);
stream->bufPtr++; stream->dataStart++;
return result; return result;
} }
@@ -283,18 +272,20 @@ uio_fputc(int c, uio_Stream *stream) {
assert((stream->openFlags & O_ACCMODE) != O_RDONLY); assert((stream->openFlags & O_ACCMODE) != O_RDONLY);
assert(c >= 0 && c <= 255); assert(c >= 0 && c <= 255);
if (stream->writeStart == NULL) uio_assertWriteSanity(stream);
stream->writeStart = stream->bufPtr; stream->operation = uio_StreamOperation_write;
if (stream->bufPtr == stream->bufEnd) {
if (stream->dataEnd == stream->bufEnd) {
// The buffer is full. Flush it out. // The buffer is full. Flush it out.
if (uio_Stream_flushWriteBuffer(stream) == -1) { if (uio_Stream_flushWriteBuffer(stream) == -1) {
// errno is set // errno is set
// Error status (for ferror()) is set.
return EOF; return EOF;
} }
} }
*(unsigned char *) stream->bufPtr = (unsigned char) c; *(unsigned char *) stream->dataEnd = (unsigned char) c;
stream->bufPtr++; stream->dataEnd++;
return c; return c;
} }
@@ -312,78 +303,46 @@ int
uio_fseek(uio_Stream *stream, long offset, int whence) { uio_fseek(uio_Stream *stream, long offset, int whence) {
int newPos; 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) { if (uio_Stream_flushWriteBuffer(stream) == -1) {
// errno is set // errno is set
return -1; 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); newPos = uio_lseek(stream->handle, offset, whence);
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);
if (newPos == -1) { if (newPos == -1) {
// errno is set // errno is set
return -1; return -1;
} }
stream->seekLow = newPos;
stream->status = uio_Stream_STATUS_OK; stream->status = uio_Stream_STATUS_OK;
// Clear error or end-of-file flag. // 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; return 0;
} }
long long
uio_ftell(uio_Stream *stream) { 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 // 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 bytesToWrite;
ssize_t bytesWritten; 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; bytesToWrite = size * nmemb;
if (stream->writeStart == NULL) if (bytesToWrite < stream->bufEnd - stream->dataEnd) {
stream->writeStart = stream->bufPtr;
if (bytesToWrite < stream->bufEnd - stream->bufPtr) {
// There's enough space in the write buffer to store everything. // There's enough space in the write buffer to store everything.
memcpy(stream->bufPtr, buf, bytesToWrite); memcpy(stream->dataEnd, buf, bytesToWrite);
stream->bufPtr += bytesToWrite; stream->dataEnd += bytesToWrite;
return nmemb; return nmemb;
} }
// Not enough space in the write buffer to write everything. // 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) { if (uio_Stream_flushWriteBuffer(stream) == -1) {
// errno is set // errno is set
// Error status (for ferror()) is set.
return 0; return 0;
} }
if (stream->openFlags & O_APPEND) { if (bytesToWrite < stream->bufEnd - stream->dataEnd) {
// If a file is opened in append mode, the file position indicator // The now empty write buffer is large enough to store everything.
// is moved to the end of the file before writing. memcpy(stream->dataEnd, buf, bytesToWrite);
// We leave that up to the physical layer. stream->dataEnd += bytesToWrite;
} else { return nmemb;
// 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);
} }
// 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); bytesWritten = uio_write(stream->handle, buf, bytesToWrite);
if (bytesWritten != bytesToWrite) { if (bytesWritten != bytesToWrite) {
stream->status = uio_Stream_STATUS_ERROR; stream->status = uio_Stream_STATUS_ERROR;
if (bytesWritten == -1) if (bytesWritten == -1)
return 0; 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) if (bytesWritten == bytesToWrite)
return nmemb; return nmemb;
return (size_t) bytesWritten / size; return (size_t) bytesWritten / size;
@@ -469,8 +404,16 @@ uio_fwrite(const void *buf, size_t size, size_t nmemb, uio_Stream *stream) {
int int
uio_fflush(uio_Stream *stream) { uio_fflush(uio_Stream *stream) {
assert(stream != NULL); 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 int
@@ -494,85 +437,85 @@ uio_streamHandle(uio_Stream *stream) {
return stream->handle; 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 static int
uio_Stream_flushWriteBuffer(uio_Stream *stream) { uio_Stream_flushWriteBuffer(uio_Stream *stream) {
ssize_t bytesWritten; ssize_t bytesWritten;
off_t newPos;
newPos = uio_lseek(stream->handle, assert(stream->operation == uio_StreamOperation_write);
(off_t) (stream->seekLow - (stream->readEnd - stream->writeStart)),
SEEK_SET);
if (newPos == -1) {
// errno is set
return -1;
}
stream->seekLow = newPos;
bytesWritten = uio_write(stream->handle, stream->writeStart, bytesWritten = uio_write(stream->handle, stream->dataStart,
stream->bufPtr - stream->writeStart); stream->dataEnd - stream->dataStart);
if (bytesWritten != stream->bufPtr - stream->writeStart) { if (bytesWritten != stream->dataEnd - stream->dataStart) {
stream->status = uio_Stream_STATUS_ERROR; stream->status = uio_Stream_STATUS_ERROR;
if (bytesWritten != -1)
stream->seekLow += bytesWritten;
return -1; return -1;
} }
stream->seekLow += bytesWritten; assert(stream->dataStart == stream->buf);
stream->writeStart = NULL; 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; 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 static int
uio_Stream_fillReadBuffer(uio_Stream *stream) { uio_Stream_fillReadBuffer(uio_Stream *stream) {
ssize_t numRead; ssize_t numRead;
assert(stream->bufPtr == stream->readEnd); assert(stream->operation == uio_StreamOperation_read);
numRead = uio_read(stream->handle, stream->buf, numRead = uio_read(stream->handle, stream->buf,
uio_Stream_BLOCK_SIZE); uio_Stream_BLOCK_SIZE);
if (numRead == -1) if (numRead == -1)
return -1; return -1;
stream->bufPtr = stream->buf; stream->dataStart = stream->buf;
stream->readEnd = stream->buf + numRead; stream->dataEnd = 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;
return 0; return 0;
} }
@@ -584,13 +527,11 @@ uio_Stream_new(uio_Handle *handle, int openFlags) {
result->handle = handle; result->handle = handle;
result->openFlags = openFlags; result->openFlags = openFlags;
result->status = uio_Stream_STATUS_OK; result->status = uio_Stream_STATUS_OK;
result->operation = uio_StreamOperation_none;
result->buf = uio_malloc(uio_Stream_BLOCK_SIZE); result->buf = uio_malloc(uio_Stream_BLOCK_SIZE);
result->bufPtr = result->buf; result->dataStart = result->buf;
result->readStart = result->buf; result->dataEnd = result->buf;
result->readEnd = result->buf;
result->writeStart = NULL;
result->bufEnd = result->buf + uio_Stream_BLOCK_SIZE; result->bufEnd = result->buf + uio_Stream_BLOCK_SIZE;
result->seekLow = 0;
return result; return result;
} }
+18 -34
View File
@@ -55,48 +55,32 @@ uio_Handle *uio_streamHandle(uio_Stream *stream);
#include <fcntl.h> #include <fcntl.h>
#include "iointrn.h" #include "iointrn.h"
/* typedef enum {
* Layout of buf: uio_StreamOperation_none,
* uio_StreamOperation_read,
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ uio_StreamOperation_write
* | | | | | | | |D|D|D|D|D|D|D|D| | | | | | | |X|X|X|X|X|X|X|X|X|X|X|X|X|X| } uio_StreamOperation;
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* ^ ^ ^ ^ ^
* +-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.
*
*/
struct uio_Stream { struct uio_Stream {
char *buf; char *buf;
char *bufPtr; // Start of the buffer.
char *readStart; char *dataStart;
// Not really used atm. // Start of the part of the buffer that is in use.
char *writeStart; char *dataEnd;
char *readEnd; // Start of the unused part of the buffer.
char *bufEnd; char *bufEnd;
off_t seekLow; // End of the buffer.
// Low(er) level file pointer. Always points to the position just // INV: buf <= dataStart <= dataEnd <= bufEnd
// past the part that's in the buffer as [readStart..readEnd]. // INV: if 'operation == uio_StreamOperation_write' then buf == dataStart
uio_Handle *handle; uio_Handle *handle;
int status; int status;
#define uio_Stream_STATUS_OK 0 #define uio_Stream_STATUS_OK 0
#define uio_Stream_STATUS_EOF 1 #define uio_Stream_STATUS_EOF 1
#define uio_Stream_STATUS_ERROR 2 #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; int openFlags;
// Flags used for opening the file. // Flags used for opening the file.
}; };