Vault
4.1
|
00001 /* 00002 Copyright c1997-2014 Trygve Isaacson. All rights reserved. 00003 This file is part of the Code Vault version 4.1 00004 http://www.bombaydigital.com/ 00005 License: MIT. See LICENSE.md in the Vault top level directory. 00006 */ 00007 00010 #include "vbufferedfilestream.h" 00011 #include "vtypes_internal.h" 00012 00013 #include "vexception.h" 00014 00015 VBufferedFileStream::VBufferedFileStream() 00016 : VAbstractFileStream() 00017 , mFile(NULL) 00018 , mCloseOnDestruct(true) 00019 { 00020 } 00021 00022 VBufferedFileStream::VBufferedFileStream(const VFSNode& node) 00023 : VAbstractFileStream(node) 00024 , mFile(NULL) 00025 , mCloseOnDestruct(true) 00026 { 00027 } 00028 00029 VBufferedFileStream::VBufferedFileStream(FILE* f, bool closeOnDestruct) 00030 : VAbstractFileStream() 00031 , mFile(f) 00032 , mCloseOnDestruct(closeOnDestruct) 00033 { 00034 } 00035 00036 VBufferedFileStream::~VBufferedFileStream() { 00037 if (mCloseOnDestruct) { 00038 VBufferedFileStream::close(); 00039 } 00040 00041 mFile = NULL; 00042 } 00043 00044 void VBufferedFileStream::setFile(FILE* f, bool closeOnDestruct) { 00045 mFile = f; 00046 mCloseOnDestruct = closeOnDestruct; 00047 } 00048 00049 void VBufferedFileStream::openReadOnly() { 00050 mFile = VFileSystem::fopen(mNode.getPath(), "rb"); 00051 00052 this->_throwIfOpenFailed("VBufferedFileStream::openReadOnly", mNode.getPath()); 00053 } 00054 00055 void VBufferedFileStream::openReadWrite() { 00056 /* 00057 The semantics of ::fopen() and ::open() run counter to the normal desire 00058 to open a file r/w and have it created if it doesn't exist. For example, 00059 mode "r" does not create the file. Mode "w" does. So instead, we check for 00060 existence first, and then open it exactly the way we intend. 00061 */ 00062 00063 mFile = VFileSystem::fopen(mNode.getPath(), mNode.exists() ? "r+b" : "w+b"); 00064 00065 this->_throwIfOpenFailed("VBufferedFileStream::openReadWrite", mNode.getPath()); 00066 } 00067 00068 void VBufferedFileStream::openWrite() { 00069 mFile = VFileSystem::fopen(mNode.getPath(), "wb"); 00070 00071 this->_throwIfOpenFailed("VBufferedFileStream::openWrite", mNode.getPath()); 00072 } 00073 00074 bool VBufferedFileStream::isOpen() const { 00075 return (mFile != NULL); 00076 } 00077 00078 void VBufferedFileStream::close() { 00079 if (this->isOpen()) { 00080 (void) VFileSystem::fclose(mFile); 00081 mFile = NULL; 00082 } 00083 } 00084 00085 Vs64 VBufferedFileStream::read(Vu8* targetBuffer, Vs64 numBytesToRead) { 00086 /* 00087 Most of the work here is to deal with the fact that we are providing 00088 a SInt64 count in our API, but the underlying OS API may only 00089 allow a size_t count. So we may have to read in a loop until the 00090 requested number of bytes is read or EOF is reached. In the typical 00091 case, of course, we only go through the loop once. 00092 */ 00093 00094 bool needsSizeConversion = (sizeof(Vs64) != sizeof(size_t)); 00095 Vs64 numBytesRemaining = numBytesToRead; 00096 Vs64 numBytesRead = 0; 00097 size_t requestCount; 00098 size_t actualCount; 00099 00100 do { 00101 if (needsSizeConversion) { 00102 requestCount = static_cast<size_t>(V_MIN(((Vs64)(0x7FFFFFFF)), numBytesRemaining)); 00103 } else { 00104 requestCount = static_cast<size_t>(numBytesRemaining); 00105 } 00106 00107 actualCount = VFileSystem::fread(targetBuffer + numBytesRead, 1, requestCount, mFile); 00108 00109 numBytesRead += actualCount; 00110 numBytesRemaining -= actualCount; 00111 00112 } while ((actualCount == requestCount) && (numBytesRemaining > 0)); 00113 00114 return numBytesRead; 00115 } 00116 00117 Vs64 VBufferedFileStream::write(const Vu8* buffer, Vs64 numBytesToWrite) { 00118 /* 00119 Most of the work here is to deal with the fact that we are providing 00120 a SInt64 count in our API, but the underlying OS API may only 00121 allow a size_t count. So we may have to write in a loop until the 00122 requested number of bytes is written. In the typical 00123 case, of course, we only go through the loop once. 00124 */ 00125 00126 bool needsSizeConversion = VStream::needSizeConversion(numBytesToWrite); 00127 Vs64 numBytesRemaining = numBytesToWrite; 00128 Vs64 numBytesWritten = 0; 00129 size_t requestCount; 00130 size_t actualCount; 00131 00132 do { 00133 if (needsSizeConversion) { 00134 requestCount = static_cast<size_t>(V_MIN((static_cast<Vs64>(LONG_MAX)), numBytesRemaining)); 00135 } else { 00136 requestCount = static_cast<size_t>(numBytesRemaining); 00137 } 00138 00139 actualCount = VFileSystem::fwrite(buffer + numBytesWritten, 1, requestCount, mFile); 00140 00141 numBytesWritten += actualCount; 00142 numBytesRemaining -= actualCount; 00143 00144 } while ((actualCount == requestCount) && (numBytesRemaining > 0)); 00145 00146 if (numBytesWritten != numBytesToWrite) { 00147 VString path; 00148 mNode.getPath(path); 00149 00150 throw VException(VSystemError(), VSTRING_FORMAT("VBufferedFileStream::write to '%s' only wrote " VSTRING_FORMATTER_S64 " of " VSTRING_FORMATTER_S64 " requested bytes.", path.chars(), numBytesWritten, numBytesToWrite)); 00151 } 00152 00153 return numBytesWritten; 00154 } 00155 00156 void VBufferedFileStream::flush() { 00157 int result = VFileSystem::fflush(mFile); 00158 00159 if (result != 0) { 00160 VString path; 00161 mNode.getPath(path); 00162 throw VException(VSystemError(), VSTRING_FORMAT("VBufferedFileStream::flush to '%s' failed with result %d.", path.chars(), result)); 00163 } 00164 } 00165 00166 bool VBufferedFileStream::skip(Vs64 numBytesToSkip) { 00167 /* 00168 Most of the work here is to deal with the fact that we are providing 00169 a SInt64 count in our API, but the underlying OS API may only 00170 allow a long count. So we may have to seek in a loop until the 00171 requested offset is seeked. In the typical case, of course, we only go 00172 through the loop once. 00173 */ 00174 00175 bool success; 00176 bool needsSizeConversion = VStream::needSizeConversion(numBytesToSkip); 00177 Vs64 numBytesRemaining = numBytesToSkip; 00178 Vs64 requestCount; 00179 00180 do { 00181 if (needsSizeConversion) { 00182 requestCount = V_MIN((static_cast<Vs64>(LONG_MAX)), numBytesRemaining); 00183 } else { 00184 requestCount = numBytesRemaining; 00185 } 00186 00187 success = this->seek(numBytesToSkip, SEEK_CUR); 00188 00189 numBytesRemaining -= requestCount; 00190 00191 } while (success && (numBytesRemaining > 0)); 00192 00193 return success; 00194 } 00195 00196 bool VBufferedFileStream::seek(Vs64 offset, int whence) { 00197 // FIXME: need to deal with Vs64-to-long conversion 00198 return (VFileSystem::fseek(mFile, static_cast<long>(offset), whence) == 0); 00199 } 00200 00201 Vs64 VBufferedFileStream::getIOOffset() const { 00202 return VFileSystem::ftell(mFile); 00203 } 00204 00205 Vs64 VBufferedFileStream::available() const { 00206 Vs64 currentOffset = this->getIOOffset(); 00207 Vs64 eofOffset; 00208 00209 // const_cast: WORKAROUND. Save/restore state. 00210 const_cast<VBufferedFileStream*>(this)->seek(0, SEEK_END); 00211 eofOffset = this->getIOOffset(); 00212 const_cast<VBufferedFileStream*>(this)->seek(currentOffset, SEEK_SET); // restore original position 00213 00214 return eofOffset - currentOffset; 00215 } 00216