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 "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