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 "vdirectiofilestream.h" 00011 #include "vtypes_internal.h" 00012 00013 #include "vexception.h" 00014 00015 VDirectIOFileStream::VDirectIOFileStream() 00016 : VAbstractFileStream() 00017 , mFile(-1) 00018 , mCloseOnDestruct(true) 00019 { 00020 } 00021 00022 VDirectIOFileStream::VDirectIOFileStream(const VFSNode& node) 00023 : VAbstractFileStream(node) 00024 , mFile(-1) 00025 , mCloseOnDestruct(true) 00026 { 00027 } 00028 00029 VDirectIOFileStream::VDirectIOFileStream(int fd, bool closeOnDestruct) 00030 : VAbstractFileStream() 00031 , mFile(fd) 00032 , mCloseOnDestruct(closeOnDestruct) 00033 { 00034 } 00035 00036 VDirectIOFileStream::~VDirectIOFileStream() { 00037 if (mCloseOnDestruct) { 00038 VDirectIOFileStream::close(); 00039 } 00040 } 00041 00042 void VDirectIOFileStream::setFile(int fd, bool closeOnDestruct) { 00043 mFile = fd; 00044 mCloseOnDestruct = closeOnDestruct; 00045 } 00046 00047 void VDirectIOFileStream::openReadOnly() { 00048 mFile = VFileSystem::open(mNode.getPath(), READ_ONLY_MODE); 00049 00050 this->_throwIfOpenFailed("VDirectIOFileStream::openReadOnly", mNode.getPath()); 00051 } 00052 00053 void VDirectIOFileStream::openReadWrite() { 00054 /* 00055 The semantics of ::fopen() and ::open() run counter to the normal desire 00056 to open a file r/w and have it created if it doesn't exist. For example, 00057 mode "r" does not create the file. Mode "w" does. So instead, we check for 00058 existence first, and then open it exactly the way we intend. 00059 */ 00060 00061 mFile = VFileSystem::open(mNode.getPath(), mNode.exists() ? READWRITE_MODE : WRITE_CREATE_MODE); 00062 00063 this->_throwIfOpenFailed("VDirectIOFileStream::openReadWrite", mNode.getPath()); 00064 } 00065 00066 void VDirectIOFileStream::openWrite() { 00067 mFile = VFileSystem::open(mNode.getPath(), WRITE_CREATE_MODE); 00068 00069 this->_throwIfOpenFailed("VDirectIOFileStream::openWrite", mNode.getPath()); 00070 } 00071 00072 bool VDirectIOFileStream::isOpen() const { 00073 return (mFile != -1); 00074 } 00075 00076 void VDirectIOFileStream::close() { 00077 if (this->isOpen()) { 00078 (void) VFileSystem::close(mFile); 00079 mFile = -1; 00080 } 00081 } 00082 00083 Vs64 VDirectIOFileStream::read(Vu8* targetBuffer, Vs64 numBytesToRead) { 00084 /* 00085 Most of the work here is to deal with the fact that we are providing 00086 a SInt64 count in our API, but the underlying OS API may only 00087 allow a size_t count. So we may have to read in a loop until the 00088 requested number of bytes is read or EOF is reached. In the typical 00089 case, of course, we only go through the loop once. 00090 */ 00091 00092 bool needsSizeConversion = VStream::needSizeConversion(numBytesToRead); 00093 Vs64 numBytesRemaining = numBytesToRead; 00094 Vs64 numBytesRead = 0; 00095 size_t requestCount; 00096 size_t actualCount; 00097 00098 // FIXME: If needsSizeConversion is false, just do a read w/o the excess baggage here. 00099 // Also, we can set it to false if the actual size fits in 31 bits, as in VStream::copyMemory. 00100 do { 00101 if (needsSizeConversion) { 00102 requestCount = static_cast<size_t>(V_MIN((CONST_S64(0x7FFFFFFF)), numBytesRemaining)); 00103 } else { 00104 requestCount = static_cast<size_t>(numBytesRemaining); 00105 } 00106 00107 actualCount = static_cast<size_t>(VFileSystem::read(mFile, targetBuffer + numBytesRead, requestCount)); 00108 00109 numBytesRead += actualCount; 00110 numBytesRemaining -= actualCount; 00111 00112 } while ((actualCount == requestCount) && (numBytesRemaining > 0)); 00113 00114 return numBytesRead; 00115 } 00116 00117 Vs64 VDirectIOFileStream::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 // FIXME: If needsSizeConversion is false, just do a write w/o the excess baggage here. 00133 // Also, we can set it to false if the actual size fits in 31 bits, as in VStream::copyMemory. 00134 do { 00135 if (needsSizeConversion) { 00136 requestCount = static_cast<size_t>(V_MIN((static_cast<Vs64>(LONG_MAX)), numBytesRemaining)); 00137 } else { 00138 requestCount = static_cast<size_t>(numBytesRemaining); 00139 } 00140 00141 actualCount = static_cast<size_t>(VFileSystem::write(mFile, buffer + numBytesWritten, requestCount)); 00142 00143 numBytesWritten += actualCount; 00144 numBytesRemaining -= actualCount; 00145 00146 } while ((actualCount == requestCount) && (numBytesRemaining > 0)); 00147 00148 if (numBytesWritten != numBytesToWrite) { 00149 VString path; 00150 mNode.getPath(path); 00151 00152 throw VException(VSystemError(), VSTRING_FORMAT("VDirectIOFileStream::write to '%s' only wrote " VSTRING_FORMATTER_S64 " of " VSTRING_FORMATTER_S64 " requested bytes.", path.chars(), numBytesWritten, numBytesToWrite)); 00153 } 00154 00155 return numBytesWritten; 00156 } 00157 00158 void VDirectIOFileStream::flush() { 00159 // Unbuffered file writes have no flush mechanism since they are not buffered. 00160 } 00161 00162 bool VDirectIOFileStream::skip(Vs64 numBytesToSkip) { 00163 /* 00164 Most of the work here is to deal with the fact that we are providing 00165 a SInt64 count in our API, but the underlying OS API may only 00166 allow a long count. So we may have to seek in a loop until the 00167 requested offset is seeked. In the typical case, of course, we only go 00168 through the loop once. 00169 */ 00170 00171 bool success; 00172 bool needsSizeConversion = VStream::needSizeConversion(numBytesToSkip); 00173 Vs64 numBytesRemaining = numBytesToSkip; 00174 Vs64 requestCount; 00175 00176 // FIXME: If needsSizeConversion is false, just do a seek w/o the excess baggage here. 00177 // Also, we can set it to false if the actual size fits in 31 bits, as in VStream::copyMemory. 00178 // FIXME: This calls VDirectIOFileStream::seek below, which is not as savvy as this--should it be? 00179 do { 00180 if (needsSizeConversion) { 00181 requestCount = V_MIN((static_cast<Vs64>(LONG_MAX)), numBytesRemaining); 00182 } else { 00183 requestCount = numBytesRemaining; 00184 } 00185 00186 success = this->seek(requestCount, SEEK_CUR); 00187 00188 numBytesRemaining -= requestCount; 00189 00190 } while (success && (numBytesRemaining > 0)); 00191 00192 return success; 00193 } 00194 00195 bool VDirectIOFileStream::seek(Vs64 offset, int whence) { 00196 // FIXME: need to deal with Vs64-to-off_t conversion 00197 return (VFileSystem::lseek(mFile, static_cast<off_t>(offset), whence) == 0); 00198 } 00199 00200 Vs64 VDirectIOFileStream::getIOOffset() const { 00201 return VFileSystem::lseek(mFile, static_cast<off_t>(0), SEEK_CUR); 00202 } 00203 00204 Vs64 VDirectIOFileStream::available() const { 00205 Vs64 currentOffset = this->getIOOffset(); 00206 Vs64 eofOffset; 00207 00208 // const_cast: WORKAROUND. Save/restore state. 00209 const_cast<VDirectIOFileStream*>(this)->seek(0, SEEK_END); 00210 eofOffset = this->getIOOffset(); 00211 const_cast<VDirectIOFileStream*>(this)->seek(currentOffset, SEEK_SET); // restore original position 00212 00213 return eofOffset - currentOffset; 00214 } 00215