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 "vstream.h" 00011 00012 #include "vexception.h" 00013 #include "viostream.h" 00014 00015 VStream::VStream() 00016 : mName() 00017 { 00018 } 00019 00020 VStream::VStream(const VString& name) 00021 : mName(name) 00022 { 00023 } 00024 00025 void VStream::readGuaranteed(Vu8* targetBuffer, Vs64 numBytesToRead) { 00026 Vs64 numBytesRead = this->read(targetBuffer, numBytesToRead); 00027 00028 if (numBytesRead != numBytesToRead) { 00029 throw VEOFException(VSTRING_FORMAT("VStream::readGuaranteed encountered end of stream. Read " VSTRING_FORMATTER_S64 " of " VSTRING_FORMATTER_S64 " bytes.", numBytesRead, numBytesToRead)); 00030 } 00031 } 00032 00033 Vu8 VStream::readGuaranteedByte() { 00034 Vu8 theByte; 00035 this->readGuaranteed(&theByte, 1); 00036 return theByte; 00037 } 00038 00039 // static 00040 Vs64 VStream::streamCopy(VStream& fromStream, VStream& toStream, Vs64 numBytesToCopy, Vs64 tempBufferSize) { 00041 Vs64 numBytesCopied = 0; 00042 00043 /* 00044 First we figure out which (if either) of the streams can give us a buffer 00045 pointer. Either or both of these may be NULL. 00046 */ 00047 Vu8* fromBuffer = fromStream._getReadIOPtr(); 00048 Vu8* toBuffer = toStream._getWriteIOPtr(); 00049 00050 /* 00051 If the source stream gave us a buffer to read from, we have to ask it 00052 how much data it really has, so we know how much we're really going to be 00053 copying. 00054 */ 00055 if (fromBuffer != NULL) { 00056 numBytesToCopy = fromStream._prepareToRead(numBytesToCopy); 00057 } 00058 00059 /* 00060 If the target stream gave us a buffer to write to, we have to ask it 00061 again after first giving it a chance to expand the buffer to fit the 00062 requested copy size. 00063 */ 00064 if (toBuffer != NULL) { 00065 toStream._prepareToWrite(numBytesToCopy); 00066 toBuffer = toStream._getWriteIOPtr(); 00067 } 00068 00069 /* 00070 Now we can proceed with the copy. The matrix of possibities is the 00071 two possible sources (buffer or stream) and the two possible targets 00072 (buffer or stream). We handle each case optimally. 00073 */ 00074 if ((fromBuffer == NULL) && (toBuffer != NULL)) { 00075 // stream-to-buffer copy 00076 numBytesCopied = fromStream.read(toBuffer, numBytesToCopy); 00077 toStream._finishWrite(numBytesCopied); 00078 } else if ((fromBuffer != NULL) && (toBuffer == NULL)) { 00079 // buffer-to-stream copy 00080 numBytesCopied = toStream.write(fromBuffer, numBytesToCopy); 00081 fromStream._finishRead(numBytesCopied); 00082 } else if ((fromBuffer != NULL) && (toBuffer != NULL)) { 00083 // buffer-to-buffer copy 00084 VStream::copyMemory(toBuffer, fromBuffer, numBytesToCopy); 00085 numBytesCopied = numBytesToCopy; 00086 00087 fromStream._finishRead(numBytesCopied); 00088 toStream._finishWrite(numBytesCopied); 00089 } else { 00090 /* 00091 Worst case scenario: direct copy between streams without their own 00092 buffers, so we have to create a buffer to do the transfer. 00093 */ 00094 00095 Vu8* tempBuffer; 00096 Vs64 numBytesRemaining; 00097 Vs64 numTempBytesToCopy; 00098 Vs64 numTempBytesRead; 00099 Vs64 numTempBytesWritten; 00100 00101 numBytesRemaining = numBytesToCopy; 00102 tempBufferSize = V_MIN(numBytesToCopy, tempBufferSize); 00103 00104 tempBuffer = VStream::newNewBuffer(tempBufferSize); 00105 00106 while (numBytesRemaining > 0) { 00107 numTempBytesToCopy = V_MIN(numBytesRemaining, tempBufferSize); 00108 00109 numTempBytesRead = fromStream.read(tempBuffer, numTempBytesToCopy); 00110 00111 // If we detect EOF, we're done. 00112 if (numTempBytesRead == 0) { 00113 break; 00114 } 00115 00116 numTempBytesWritten = toStream.write(tempBuffer, numTempBytesRead); 00117 00118 numBytesRemaining -= numTempBytesWritten; 00119 numBytesCopied += numTempBytesWritten; 00120 00121 // If we couldn't write any bytes, we have a problem and should stop here. 00122 if (numTempBytesWritten == 0) { 00123 break; 00124 } 00125 } 00126 00127 delete [] tempBuffer; 00128 } 00129 00130 return numBytesCopied; 00131 } 00132 00133 // static 00134 Vs64 VStream::streamCopy(VIOStream& fromStream, VIOStream& toStream, Vs64 numBytesToCopy, Vs64 tempBufferSize) { 00135 return VStream::streamCopy(fromStream.getRawStream(), toStream.getRawStream(), numBytesToCopy, tempBufferSize); 00136 } 00137 00138 // static 00139 Vs64 VStream::streamCopy(VIOStream& fromStream, VStream& toStream, Vs64 numBytesToCopy, Vs64 tempBufferSize) { 00140 return VStream::streamCopy(fromStream.getRawStream(), toStream, numBytesToCopy, tempBufferSize); 00141 } 00142 00143 // static 00144 Vs64 VStream::streamCopy(VStream& fromStream, VIOStream& toStream, Vs64 numBytesToCopy, Vs64 tempBufferSize) { 00145 return VStream::streamCopy(fromStream, toStream.getRawStream(), numBytesToCopy, tempBufferSize); 00146 } 00147 00148 // static 00149 bool VStream::needSizeConversion(Vs64 sizeValue) { 00150 return ((sizeValue > V_MAX_S32) && (sizeof(Vs64) != sizeof(size_t))); 00151 } 00152 00153 // static 00154 void VStream::copyMemory(Vu8* toBuffer, const Vu8* fromBuffer, Vs64 numBytesToCopy) { 00155 /* 00156 The purpose of this function is simply to allow the full 64-bit length 00157 while remaining compatible with platforms where memcpy() only 00158 supports a 32-bit length. It's not just a matter of type conversion, 00159 because if the requested length actually exceeds size_t, then 00160 we must copy in a loop. (Very unlikely to actually occur in real-world 00161 scenarios, but we may as well do it correctly.) 00162 We do make one assumption: that size_t is at least 31 bits, that is 00163 that it can hold a number up to at least V_MAX_S32. 00164 If Vs64 and size_t are identical, or we're copying less than 2GB of data, 00165 then memcpy is fine! 00166 */ 00167 00168 if (! VStream::needSizeConversion(numBytesToCopy)) { 00169 // Entire copy can occur in a single call to memcpy. 00170 ::memcpy(toBuffer, fromBuffer, static_cast<size_t>(numBytesToCopy)); 00171 } else { 00172 // Need to call memcpy multiple times because numBytesToCopy is too big. 00173 Vs64 numBytesRemaining = numBytesToCopy; 00174 size_t copyChunkSize; 00175 Vu8* toPtr = toBuffer; 00176 const Vu8* fromPtr = fromBuffer; 00177 00178 do { 00179 copyChunkSize = static_cast<size_t>(V_MIN(V_MAX_S32, numBytesRemaining)); 00180 00181 ::memcpy(toPtr, fromPtr, copyChunkSize); 00182 00183 numBytesRemaining -= copyChunkSize; 00184 fromPtr += copyChunkSize; 00185 toPtr += copyChunkSize; 00186 00187 } while (numBytesRemaining > 0); 00188 } 00189 } 00190 00191 Vu8* VStream::newNewBuffer(Vs64 bufferSize) { 00192 bool fits = ((sizeof(Vs64) == sizeof(size_t)) || (bufferSize <= V_MAX_S32)); 00193 00194 if (!fits) { 00195 throw std::bad_alloc(); 00196 } 00197 00198 return new Vu8[static_cast<size_t>(bufferSize)]; // throws std::bad_alloc if we run out of memory 00199 } 00200 00201 Vu8* VStream::mallocNewBuffer(Vs64 bufferSize) { 00202 bool fits = ((sizeof(Vs64) == sizeof(size_t)) || (bufferSize <= V_MAX_S32)); 00203 00204 if (!fits) { 00205 throw std::bad_alloc(); 00206 } 00207 00208 Vu8* buffer = static_cast<Vu8*>(::malloc(static_cast<size_t>(bufferSize))); 00209 if (buffer == NULL) { 00210 throw std::bad_alloc(); // out of memory 00211 } 00212 00213 return buffer; 00214 } 00215 00216 Vu8* VStream::_getReadIOPtr() const { 00217 // To be overridden by memory-based streams. 00218 return NULL; 00219 } 00220 00221 Vu8* VStream::_getWriteIOPtr() const { 00222 // To be overridden by memory-based streams. 00223 return NULL; 00224 } 00225 00226 Vs64 VStream::_prepareToRead(Vs64 /*numBytesToRead*/) const { 00227 // To be overridden by memory-based streams. 00228 return 0; 00229 } 00230 00231 void VStream::_prepareToWrite(Vs64 /*numBytesToWrite*/) { 00232 // To be overridden by memory-based streams. 00233 } 00234 00235 void VStream::_finishRead(Vs64 /*numBytesRead*/) { 00236 // To be overridden by memory-based streams. 00237 } 00238 00239 void VStream::_finishWrite(Vs64 /*numBytesWritten*/) { 00240 // To be overridden by memory-based streams. 00241 } 00242