Vault  4.1
vstream.cpp
Go to the documentation of this file.
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 

Copyright ©1997-2014 Trygve Isaacson. All rights reserved. This documentation was generated with Doxygen.