Vault  4.1
vmemorystream.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 "vmemorystream.h"
00011 
00012 #include "vexception.h"
00013 #include "vassert.h"
00014 
00015 // Is ASSERT_INVARIANT enabled/disabled specifically for VMemoryStream?
00016 #ifdef V_ASSERT_INVARIANT_VMEMORYSTREAM_ENABLED
00017     #undef ASSERT_INVARIANT
00018     #if V_ASSERT_INVARIANT_VMEMORYSTREAM_ENABLED == 1
00019         #define ASSERT_INVARIANT() this->_assertInvariant() ///< Macro to call this->_assertInvariant().
00020     #else
00021         #define ASSERT_INVARIANT() ((void) 0) ///< No-op.
00022     #endif
00023 #endif
00024 
00025 // VMemoryStream --------------------------------------------------------------------------
00026 
00027 VMemoryStream::VMemoryStream(Vs64 initialBufferSize, Vs64 resizeIncrement)
00028     : VStream()
00029     , mBufferSize(initialBufferSize)
00030     , mIOOffset(0)
00031     , mEOFOffset(0)
00032     , mResizeIncrement(resizeIncrement)
00033     , mOwnsBuffer(true)
00034     , mAllocationType(kAllocatedByOperatorNew)
00035     , mBuffer(NULL)
00036     {
00037     mBuffer = this->_createNewBuffer(mBufferSize, mAllocationType);
00038 
00039     ASSERT_INVARIANT();
00040 }
00041 
00042 VMemoryStream::VMemoryStream(const VMemoryStream& other)
00043     : VStream(other.mName)
00044     , mBufferSize(other.mBufferSize)
00045     , mIOOffset(other.mIOOffset)
00046     , mEOFOffset(other.mEOFOffset)
00047     , mResizeIncrement(other.mResizeIncrement)
00048     , mOwnsBuffer(false)
00049     , mAllocationType(other.mAllocationType)
00050     , mBuffer(other.mBuffer)
00051     {
00052     /*
00053     All we need to do is decide how to manage the buffer, based on the ownership:
00054 
00055     1.
00056     If the other stream does NOT own the buffer, then we're all set: we're sharing a
00057     buffer that some third stream owns. We don't own it, and just point to it like the
00058     other stream.
00059 
00060     2.
00061     If the other stream owns the buffer, then we need to make a copy. It is not safe
00062     for us to simply point to the existing buffer in a non-owned fashion, because
00063     some calling code might assume it can delete the other stream, which would delete
00064     the buffer it owns that we'd still point to if we weren't deleted first.
00065     */
00066 
00067     if (other.mOwnsBuffer) {
00068         // Case 2 -- we need to make our own copy of the buffer.
00069         BufferAllocationType newAllocationType;
00070         Vu8* newBuffer = this->_createNewBuffer(mBufferSize, newAllocationType);
00071 
00072         VStream::copyMemory(newBuffer, mBuffer, mEOFOffset);
00073 
00074         mBuffer = newBuffer;
00075         mAllocationType = newAllocationType;
00076         mOwnsBuffer = true;
00077     }
00078 
00079     ASSERT_INVARIANT();
00080 }
00081 
00082 VMemoryStream::VMemoryStream(Vu8* buffer, BufferAllocationType allocationType, bool adoptsBuffer, Vs64 suppliedBufferSize, Vs64 suppliedEOFOffset, Vs64 resizeIncrement)
00083     : VStream()
00084     , mBufferSize(suppliedBufferSize)
00085     , mIOOffset(0)
00086     , mEOFOffset(suppliedEOFOffset)
00087     , mResizeIncrement(resizeIncrement)
00088     , mOwnsBuffer(adoptsBuffer)
00089     , mAllocationType(allocationType)
00090     , mBuffer(buffer)
00091     {
00092     ASSERT_INVARIANT();
00093 }
00094 
00095 VMemoryStream::~VMemoryStream() {
00096     this->_releaseBuffer();
00097 }
00098 
00099 VMemoryStream& VMemoryStream::operator=(const VMemoryStream& other) {
00100     ASSERT_INVARIANT();
00101 
00102     if (this != &other) {
00103         mName = other.mName;
00104         mBufferSize = other.mBufferSize;
00105         mIOOffset = other.mIOOffset;
00106         mEOFOffset = other.mEOFOffset;
00107         mResizeIncrement = other.mResizeIncrement;
00108         mOwnsBuffer = false;
00109         mAllocationType = other.mAllocationType;
00110         mBuffer = other.mBuffer;
00111 
00112         if (other.mOwnsBuffer) {
00113             // Case 2 -- we need to make our own copy of the buffer.
00114             BufferAllocationType newAllocationType;
00115             Vu8* newBuffer = this->_createNewBuffer(mBufferSize, newAllocationType);
00116 
00117             VStream::copyMemory(newBuffer, mBuffer, mEOFOffset);
00118 
00119             mBuffer = newBuffer;
00120             mAllocationType = newAllocationType;
00121             mOwnsBuffer = true;
00122         }
00123     }
00124 
00125     ASSERT_INVARIANT();
00126 
00127     return *this;
00128 }
00129 
00130 Vs64 VMemoryStream::read(Vu8* targetBuffer, Vs64 numBytesToRead) {
00131     ASSERT_INVARIANT();
00132 
00133     Vs64 bytesRemainingInBuffer = mEOFOffset - mIOOffset;
00134     Vs64 actualNumBytesToCopy = V_MIN(numBytesToRead, bytesRemainingInBuffer);
00135 
00136     VStream::copyMemory(targetBuffer, mBuffer + mIOOffset, actualNumBytesToCopy);
00137 
00138     this->_finishRead(actualNumBytesToCopy);
00139 
00140     ASSERT_INVARIANT();
00141 
00142     return actualNumBytesToCopy;
00143 }
00144 
00145 Vs64 VMemoryStream::write(const Vu8* buffer, Vs64 numBytesToWrite) {
00146     ASSERT_INVARIANT();
00147 
00148     this->_prepareToWrite(numBytesToWrite);    // throws if we can't write requested size
00149 
00150     VStream::copyMemory(mBuffer + mIOOffset, buffer, numBytesToWrite);
00151 
00152     this->_finishWrite(numBytesToWrite);
00153 
00154     ASSERT_INVARIANT();
00155 
00156     return numBytesToWrite;
00157 }
00158 
00159 void VMemoryStream::flush() {
00160     // Nothing to flush.
00161 }
00162 
00163 bool VMemoryStream::skip(Vs64 numBytesToSkip) {
00164     ASSERT_INVARIANT();
00165 
00166     Vs64 bytesRemainingInBuffer = mEOFOffset - mIOOffset;
00167     Vs64 actualNumBytesToSkip = V_MIN(numBytesToSkip, bytesRemainingInBuffer);
00168 
00169     mIOOffset += actualNumBytesToSkip;
00170 
00171     ASSERT_INVARIANT();
00172 
00173     return (numBytesToSkip == actualNumBytesToSkip);
00174 }
00175 
00176 bool VMemoryStream::seek(Vs64 offset, int whence) {
00177     ASSERT_INVARIANT();
00178 
00179     Vs64 requestedOffset;
00180     Vs64 constrainedOffset;
00181 
00182     switch (whence) {
00183         case SEEK_SET:
00184             requestedOffset = offset;
00185             break;
00186 
00187         case SEEK_CUR:
00188             requestedOffset = mIOOffset + offset;
00189             break;
00190 
00191         case SEEK_END:
00192             requestedOffset = mEOFOffset;
00193             break;
00194 
00195         default:
00196             requestedOffset = CONST_S64(0);
00197             break;
00198     }
00199 
00200     if (requestedOffset < 0) {
00201         constrainedOffset = 0;
00202     } else if (requestedOffset > mEOFOffset) {
00203         constrainedOffset = requestedOffset;
00204 
00205         // write zeroes as we extend the buffer
00206         Vs64 numZeroesToWrite = requestedOffset - mEOFOffset;
00207         Vs64 oldEOF = mEOFOffset;
00208 
00209         mIOOffset = mEOFOffset;
00210         this->_prepareToWrite(numZeroesToWrite);
00211 
00212         while (oldEOF < mEOFOffset) {
00213             *(mBuffer + mIOOffset) = 0;
00214             oldEOF++;
00215         }
00216 
00217         this->_finishWrite(numZeroesToWrite);
00218     } else {
00219         constrainedOffset = requestedOffset;
00220     }
00221 
00222     mIOOffset = constrainedOffset;
00223 
00224     ASSERT_INVARIANT();
00225 
00226     return (constrainedOffset == requestedOffset);
00227 }
00228 
00229 Vs64 VMemoryStream::getIOOffset() const {
00230     ASSERT_INVARIANT();
00231 
00232     return mIOOffset;
00233 }
00234 
00235 Vs64 VMemoryStream::available() const {
00236     return mEOFOffset - mIOOffset;
00237 }
00238 
00239 Vu8* VMemoryStream::_getReadIOPtr() const {
00240     /*
00241     For VMemoryStream, there is no distinction between read and write
00242     i/o offset, so they are the same, and both read and write copying
00243     is supported.
00244     */
00245     ASSERT_INVARIANT();
00246 
00247     return mBuffer + mIOOffset;
00248 }
00249 
00250 Vu8* VMemoryStream::_getWriteIOPtr() const {
00251     /*
00252     For VMemoryStream, there is no distinction between read and write
00253     i/o offset, so they are the same, and both read and write copying
00254     is supported.
00255     */
00256     ASSERT_INVARIANT();
00257 
00258     return mBuffer + mIOOffset;
00259 }
00260 
00261 Vs64 VMemoryStream::_prepareToRead(Vs64 numBytesToRead) const {
00262     ASSERT_INVARIANT();
00263 
00264     Vs64 bytesRemainingInBuffer = mEOFOffset - mIOOffset;
00265     Vs64 actualNumBytesToRead = V_MIN(numBytesToRead, bytesRemainingInBuffer);
00266 
00267     return actualNumBytesToRead;
00268 }
00269 
00270 void VMemoryStream::_prepareToWrite(Vs64 numBytesToWrite) {
00271     ASSERT_INVARIANT();
00272 
00273     Vs64 requiredBufferSize = mIOOffset + numBytesToWrite;
00274 
00275     if (requiredBufferSize > mBufferSize) {
00276         // If we don't own the buffer, we are not permitted to reallocate it, so we throw
00277         // an EOF exception.
00278         if (! mOwnsBuffer) {
00279             throw VEOFException("VMemoryStream::_prepareToWrite: Invalid attempt to expand non-owned buffer.");
00280         }
00281 
00282         // Reallocate a larger buffer, copy old contents to it.
00283         Vs64 newBufferSize;
00284 
00285         if (mResizeIncrement == kIncrement2x) {
00286             // Double the size repeatedly until large enough.
00287             newBufferSize = mBufferSize;
00288 
00289             // Just in case caller was dumb enough to ask us to double
00290             // an initial size of zero, pick something...
00291             if (newBufferSize == 0) {
00292                 newBufferSize = 1024;
00293             }
00294 
00295             do {
00296                 newBufferSize = 2 * newBufferSize;
00297             } while (newBufferSize < requiredBufferSize);
00298 
00299         } else {
00300             // Grow to fit requirement, but if necessary round up to
00301             // align on increment boundary.
00302             newBufferSize = requiredBufferSize;
00303 
00304             Vs64 misalignment = newBufferSize % mResizeIncrement;
00305 
00306             if (misalignment != 0) {
00307                 newBufferSize += (mResizeIncrement - misalignment);
00308             }
00309         }
00310 
00311         BufferAllocationType newAllocationType;
00312         Vu8* newBuffer = this->_createNewBuffer(newBufferSize, newAllocationType);
00313 
00314         VStream::copyMemory(newBuffer, mBuffer, mEOFOffset);
00315 
00316         this->_releaseBuffer();
00317         mBuffer = newBuffer;
00318         mBufferSize = newBufferSize;
00319         mAllocationType = newAllocationType;
00320     }
00321 
00322     ASSERT_INVARIANT();
00323 }
00324 
00325 void VMemoryStream::_finishRead(Vs64 numBytesRead) {
00326     ASSERT_INVARIANT();
00327 
00328     mIOOffset += numBytesRead;
00329 
00330     ASSERT_INVARIANT();
00331 }
00332 
00333 void VMemoryStream::_finishWrite(Vs64 numBytesWritten) {
00334     ASSERT_INVARIANT();
00335 
00336     mIOOffset += numBytesWritten;
00337 
00338     // If we advanced past "eof", move eof forward.
00339     if (mIOOffset > mEOFOffset) {
00340         mEOFOffset = mIOOffset;
00341     }
00342 
00343     ASSERT_INVARIANT();
00344 }
00345 
00346 void VMemoryStream::adoptBuffer(Vu8* buffer, BufferAllocationType allocationType, bool adoptsBuffer, Vs64 suppliedBufferSize, Vs64 suppliedEOFOffset) {
00347     ASSERT_INVARIANT();
00348 
00349     this->_releaseBuffer();
00350 
00351     mBufferSize = suppliedBufferSize;
00352     mEOFOffset = suppliedEOFOffset;
00353     mIOOffset = 0;
00354     mOwnsBuffer = adoptsBuffer;
00355     mAllocationType = allocationType;
00356 
00357     mBuffer = buffer;
00358 
00359     ASSERT_INVARIANT();
00360 }
00361 
00362 Vu8* VMemoryStream::getBuffer() const {
00363     ASSERT_INVARIANT();
00364 
00365     return mBuffer;
00366 }
00367 
00368 void VMemoryStream::orphanBuffer() {
00369     ASSERT_INVARIANT();
00370 
00371     mOwnsBuffer = false;
00372 }
00373 
00374 Vs64 VMemoryStream::getBufferSize() const {
00375     ASSERT_INVARIANT();
00376 
00377     return mBufferSize;
00378 }
00379 
00380 Vs64 VMemoryStream::getEOFOffset() const {
00381     ASSERT_INVARIANT();
00382 
00383     return mEOFOffset;
00384 }
00385 
00386 void VMemoryStream::setEOF(Vs64 eofOffset) {
00387     ASSERT_INVARIANT();
00388 
00389     mEOFOffset = V_MIN(mBufferSize, eofOffset);
00390     mIOOffset = V_MIN(mEOFOffset, mIOOffset);
00391 
00392     ASSERT_INVARIANT();
00393 }
00394 
00395 void VMemoryStream::_releaseBuffer() {
00396     if (mOwnsBuffer) {
00397         switch (mAllocationType) {
00398             case kAllocatedByOperatorNew:
00399                 delete [] mBuffer;
00400                 mBuffer = NULL;
00401                 break;
00402             case kAllocatedByMalloc:
00403                 ::free(mBuffer);
00404                 mBuffer = NULL;
00405                 break;
00406             case kAllocatedOnStack:
00407                 mBuffer = NULL;
00408                 break;
00409             case kAllocatedUnknown:
00410                 mBuffer = NULL;
00411                 break;
00412             default:
00413                 break;
00414         }
00415     } else {
00416         mBuffer = NULL;
00417     }
00418 }
00419 
00420 Vu8* VMemoryStream::_createNewBuffer(Vs64 bufferSize, BufferAllocationType& newAllocationType) {
00421     Vu8* buffer = NULL;
00422     newAllocationType = mAllocationType; // only stack case changes this below
00423     switch (mAllocationType) {
00424         case kAllocatedByOperatorNew:
00425             buffer = VStream::newNewBuffer(bufferSize);
00426             break;
00427         case kAllocatedByMalloc:
00428             buffer = VStream::mallocNewBuffer(bufferSize);
00429             break;
00430         case kAllocatedOnStack:
00431         case kAllocatedUnknown:
00432             buffer = VStream::newNewBuffer(bufferSize);
00433             newAllocationType = kAllocatedByOperatorNew;
00434             break;
00435         default:
00436             throw VStackTraceException(VSTRING_FORMAT("VMemoryStream::_createNewBuffer: Invalid allocation type %d.", (int) mAllocationType));
00437             break;
00438     }
00439 
00440     return buffer;
00441 }
00442 
00443 void VMemoryStream::_assertInvariant() const {
00444     VASSERT_NOT_NULL(mBuffer);
00445     VASSERT_NOT_EQUAL(mBuffer, VCPP_DEBUG_BAD_POINTER_VALUE);
00446     VASSERT_GREATER_THAN_OR_EQUAL(mBufferSize, 0);
00447     VASSERT_LESS_THAN_OR_EQUAL(mEOFOffset, mBufferSize);
00448     VASSERT_LESS_THAN_OR_EQUAL(mIOOffset, mEOFOffset);
00449     VASSERT_VALUE((mAllocationType == kAllocatedByOperatorNew || mAllocationType == kAllocatedByMalloc || mAllocationType == kAllocatedOnStack || mAllocationType == kAllocatedUnknown), mAllocationType, VSTRING_INT((int)mAllocationType));
00450 }
00451 
00452 bool operator==(const VMemoryStream& m1, const VMemoryStream& m2) {
00453     Vs64 length = m1.getEOFOffset();
00454 
00455     // If the lengths don't match, we consider the streams not equal.
00456     if (m2.getEOFOffset() != length) {
00457         return false;
00458     }
00459 
00460     // If the length is zero, then we consider the streams equal and don't
00461     // need to call memcmp.
00462     if (length == 0) {
00463         return true;
00464     }
00465 
00466     // If the lengths fit size_t or size_t is 64 bits, we can just call memcmp once.
00467     if (! VStream::needSizeConversion(length)) {
00468         return ::memcmp(m1.getBuffer(), m2.getBuffer(), static_cast<size_t>(m1.getEOFOffset())) == 0;
00469     }
00470 
00471     /*
00472     If we get here, then we have too much data for memcmp to handle in one
00473     shot. We need to loop over chunks of data sized that memcmp can handle,
00474     until we finish or find an inequality.
00475     */
00476     bool    equalSoFar = true;
00477     Vs64    numBytesRemaining = length;
00478     Vs64    offset = 0;
00479     size_t  compareChunkSize;
00480 
00481     do {
00482         compareChunkSize = static_cast<size_t>(V_MIN(V_MAX_S32, numBytesRemaining));
00483 
00484         equalSoFar = ::memcmp(m1.getBuffer() + offset, m2.getBuffer() + offset, compareChunkSize) == 0;
00485 
00486         numBytesRemaining -= compareChunkSize;
00487 
00488     } while (equalSoFar && numBytesRemaining > 0);
00489 
00490     return equalSoFar;
00491 }
00492 
00493 // VReadOnlyMemoryStream --------------------------------------------------------------------------
00494 
00495 VReadOnlyMemoryStream::VReadOnlyMemoryStream(Vu8* buffer, Vs64 suppliedEOFOffset) :
00496     VMemoryStream(buffer, kAllocatedUnknown, false, suppliedEOFOffset, suppliedEOFOffset) {
00497 }
00498 
00499 VReadOnlyMemoryStream::VReadOnlyMemoryStream(const VReadOnlyMemoryStream& other) :
00500     VMemoryStream(other) {
00501 }
00502 
00503 VReadOnlyMemoryStream& VReadOnlyMemoryStream::operator=(const VReadOnlyMemoryStream& other) {
00504     VMemoryStream::operator=(other);
00505     return *this;
00506 }
00507 
00508 void VReadOnlyMemoryStream::adoptBuffer(Vu8* buffer, BufferAllocationType allocationType, Vs64 suppliedBufferSize, Vs64 suppliedEOFOffset) {
00509     VMemoryStream::adoptBuffer(buffer, allocationType, false, suppliedBufferSize, suppliedEOFOffset);
00510 }
00511 
00512 Vs64 VReadOnlyMemoryStream::write(const Vu8* /*buffer*/, Vs64 /*numBytesToWrite*/) {
00513     throw VEOFException("VReadOnlyMemoryStream::write: Invalid attempt to write to a read-only stream.");
00514 }
00515 
00516 void VReadOnlyMemoryStream::flush() {
00517     throw VEOFException("VReadOnlyMemoryStream::flush: Invalid attempt to flush a read-only stream.");
00518 }
00519 

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