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 "vstring.h" 00011 #include "vtypes_internal.h" 00012 00013 #include "vchar.h" 00014 #include "vcodepoint.h" 00015 #include "vexception.h" 00016 #include "vlogger.h" 00017 00018 #ifndef V_EFFICIENT_SPRINTF 00019 #include "vmutex.h" 00020 #include "vmutexlocker.h" 00021 #endif /* V_EFFICIENT_SPRINTF */ 00022 00023 V_STATIC_INIT_TRACE 00024 00025 #undef strlen 00026 #undef strcmp 00027 #undef sscanf 00028 00029 // Is ASSERT_INVARIANT enabled/disabled specifically for VString? 00030 #ifdef V_ASSERT_INVARIANT_VSTRING_ENABLED 00031 #undef ASSERT_INVARIANT 00032 #if V_ASSERT_INVARIANT_VSTRING_ENABLED == 1 00033 #define ASSERT_INVARIANT() this->_assertInvariant() ///< Macro to call this->_assertInvariant(). 00034 #else 00035 #define ASSERT_INVARIANT() ((void) 0) ///< No-op. 00036 #endif 00037 #endif 00038 00039 /* 00040 The "empty string" constant constructs to an empty string. 00041 When you want to pass "" to a function that takes a "const VString&" parameter, 00042 it's much more efficient to pass the empty VString constant because it avoids 00043 constructing a temporary empty VString object on the fly. 00044 */ 00045 // static 00046 const VString& VString::EMPTY() { 00047 static const VString kEmptyString; 00048 return kEmptyString; 00049 } 00050 00051 // static 00052 const VString& VString::NATIVE_LINE_ENDING() { 00053 #ifdef VPLATFORM_WIN 00054 static const VString kLineEndingString(VSTRING_ARGS("%c%c", (char) 0x0D, (char) 0x0A)); 00055 #else /* Unix and Mac OS X use Unix style */ 00056 static const VString kLineEndingString((char) 0x0A); 00057 #endif /* VPLATFORM_WIN */ 00058 return kLineEndingString; 00059 } 00060 00061 // static 00062 const VString& VString::UNIX_LINE_ENDING() { 00063 static const VString kUnixLineEndingString((char) 0x0A); 00064 return kUnixLineEndingString; 00065 } 00066 00067 // static 00068 const VString& VString::MAC_CLASSIC_LINE_ENDING() { 00069 static const VString kMacClassicLineEndingString((char) 0x0D); 00070 return kMacClassicLineEndingString; 00071 } 00072 00073 // static 00074 const VString& VString::DOS_LINE_ENDING() { 00075 static const VString kDOSLineEndingString(VSTRING_ARGS("%c%c", (char) 0x0D, (char) 0x0A)); 00076 return kDOSLineEndingString; 00077 } 00078 00079 VString::VString() 00080 { 00081 this->_construct(); 00082 00083 ASSERT_INVARIANT(); 00084 } 00085 00086 VString::VString(const VChar& c) 00087 { 00088 this->_construct(); 00089 00090 this->preflight(1); 00091 _set()[0] = c.charValue(); 00092 this->_setLength(1); 00093 00094 ASSERT_INVARIANT(); 00095 } 00096 00097 VString::VString(const VString& s) 00098 { 00099 this->_construct(); 00100 00101 int theLength = s.length(); 00102 if (theLength > 0) { 00103 this->preflight(theLength); 00104 s.copyToBuffer(_set(), theLength + 1); 00105 this->_setLength(theLength); 00106 } 00107 00108 ASSERT_INVARIANT(); 00109 } 00110 00111 VString::VString(char c) 00112 { 00113 this->_construct(); 00114 00115 this->preflight(1); 00116 _set()[0] = c; 00117 this->_setLength(1); 00118 00119 ASSERT_INVARIANT(); 00120 } 00121 00122 #ifdef VAULT_VSTRING_STRICT_FORMATTING 00123 VString::VString(const char* s) 00124 #else 00125 VString::VString(char* s) 00126 #endif /* VAULT_VSTRING_STRICT_FORMATTING */ 00127 { 00128 this->_construct(); 00129 00130 if ((s != NULL) && (s[0] != VCHAR_NULL_TERMINATOR)) { // if s is NULL or zero length, leave as initialized to empty 00131 int theLength = (int) ::strlen(s); 00132 this->preflight(theLength); 00133 ::memcpy(_set(), s, (VSizeType) (1 + theLength)); // faster than strcpy? 00134 this->_setLength(theLength); 00135 } 00136 00137 ASSERT_INVARIANT(); 00138 } 00139 00140 #ifdef VAULT_VARARG_STRING_FORMATTING_SUPPORT 00141 00142 #ifndef VAULT_VSTRING_STRICT_FORMATTING 00143 00150 static int _getStrlenIfNonFormatting(const char* s) { 00151 if (s == NULL) 00152 return -1; 00153 00154 int len = 0; 00155 while (s[len] != VCHAR_NULL_TERMINATOR) { 00156 if (s[len] == '%') 00157 return -1; 00158 00159 ++len; 00160 } 00161 00162 return len; 00163 } 00164 #endif /* VAULT_VSTRING_STRICT_FORMATTING */ 00165 00166 #ifdef VAULT_VSTRING_STRICT_FORMATTING 00167 VString::VString(Vs8 /*dummy*/, const char* formatText, ...) 00168 #else /* non-strict formatting */ 00169 VString::VString(const char* formatText, ...) 00170 #endif /* VAULT_VSTRING_STRICT_FORMATTING */ 00171 { 00172 this->_construct(); 00173 00174 if ((formatText != NULL) && (formatText[0] != VCHAR_NULL_TERMINATOR)) { // if formatText is NULL or zero length, leave as initialized to empty 00175 #ifndef VAULT_VSTRING_STRICT_FORMATTING 00176 // A common problem is constructing a string as "%", arriving that this constructor rather than the (char*) constructor. 00177 // It fails to format as desired. We can special case for this. 00178 if (formatText[0] == '%' && formatText[1] == VCHAR_NULL_TERMINATOR) { 00179 this->copyFromBuffer("%", 0, 1); 00180 } else 00181 #endif /* VAULT_VSTRING_STRICT_FORMATTING */ 00182 { 00183 #ifndef VAULT_VSTRING_STRICT_FORMATTING 00184 // Scan for formatting directives. If none exist, avoid overhead of trying to format, and just copy. 00185 int nonFormattingLen = _getStrlenIfNonFormatting(formatText); 00186 #else 00187 int nonFormattingLen = -1; // not necessary if strict formatting in use 00188 #endif /* VAULT_VSTRING_STRICT_FORMATTING */ 00189 if (nonFormattingLen == -1) { 00190 va_list args; 00191 va_start(args, formatText); 00192 00193 this->vaFormat(formatText, args); 00194 00195 va_end(args); 00196 } else { 00197 this->copyFromBuffer(formatText, 0, nonFormattingLen); 00198 } 00199 } 00200 } 00201 00202 ASSERT_INVARIANT(); 00203 } 00204 #endif /* VAULT_VARARG_STRING_FORMATTING_SUPPORT */ 00205 00206 VString::VString(const std::wstring& ws) { 00207 this->_construct(); 00208 00209 this->_assignFromUTF16WideString(ws); 00210 00211 ASSERT_INVARIANT(); 00212 } 00213 00214 #ifdef VAULT_QT_SUPPORT 00215 VString::VString(const QString& s) 00216 { 00217 this->_construct(); 00218 00219 this->copyFromBuffer(s.toUtf8(), 0, s.length()); 00220 00221 ASSERT_INVARIANT(); 00222 } 00223 #endif /* VAULT_QT_SUPPORT */ 00224 00225 #ifdef VAULT_BOOST_STRING_FORMATTING_SUPPORT 00226 VString::VString(const boost::format& fmt) 00227 { 00228 this->_construct(); 00229 00230 this->copyFromBuffer(fmt.str().c_str(), 0, static_cast<int>(fmt.size())); 00231 00232 ASSERT_INVARIANT(); 00233 } 00234 #endif /* VAULT_BOOST_STRING_FORMATTING_SUPPORT */ 00235 00236 #ifdef VAULT_CORE_FOUNDATION_SUPPORT 00237 VString::VString(const CFStringRef& s) 00238 { 00239 this->_construct(); 00240 00241 this->_assignFromCFString(s); 00242 00243 ASSERT_INVARIANT(); 00244 } 00245 #endif /* VAULT_CORE_FOUNDATION_SUPPORT */ 00246 00247 VString::VString(const VCodePoint& cp) 00248 { 00249 this->_construct(); 00250 00251 (*this) += cp.toString(); 00252 00253 ASSERT_INVARIANT(); 00254 } 00255 00256 VString::~VString() { 00257 if (!mU.mI.mUsingInternalBuffer) { 00258 delete [] mU.mX.mHeapBufferPtr; 00259 } 00260 } 00261 00262 VString& VString::operator=(const VString& s) { 00263 ASSERT_INVARIANT(); 00264 00265 if (this != &s) { 00266 int theLength = s.length(); 00267 00268 if (theLength != 0) { 00269 this->preflight(theLength); 00270 s.copyToBuffer(_set(), theLength + 1); 00271 } 00272 00273 this->_setLength(theLength); 00274 } 00275 00276 ASSERT_INVARIANT(); 00277 00278 return *this; 00279 } 00280 00281 VString& VString::operator=(const VString* s) { 00282 ASSERT_INVARIANT(); 00283 00284 if (s == NULL) { 00285 this->_setLength(0); 00286 } else if (this != s) { 00287 int theLength = s->length(); 00288 00289 if (theLength != 0) { 00290 this->preflight(theLength); 00291 s->copyToBuffer(_set(), theLength + 1); 00292 } 00293 00294 this->_setLength(theLength); 00295 } 00296 00297 ASSERT_INVARIANT(); 00298 00299 return *this; 00300 } 00301 00302 #ifdef VAULT_QT_SUPPORT 00303 VString& VString::operator=(const QString& s) { 00304 ASSERT_INVARIANT(); 00305 00306 this->copyFromBuffer(s.toUtf8(), 0, s.length()); 00307 00308 ASSERT_INVARIANT(); 00309 00310 return *this; 00311 } 00312 #endif /* VAULT_QT_SUPPORT */ 00313 00314 #ifdef VAULT_BOOST_STRING_FORMATTING_SUPPORT 00315 VString& VString::operator=(const boost::format& fmt) { 00316 ASSERT_INVARIANT(); 00317 00318 this->copyFromBuffer(fmt.str().c_str(), 0, static_cast<int>(fmt.size())); 00319 00320 ASSERT_INVARIANT(); 00321 00322 return *this; 00323 } 00324 #endif /* VAULT_BOOST_STRING_FORMATTING_SUPPORT */ 00325 00326 #ifdef VAULT_CORE_FOUNDATION_SUPPORT 00327 VString& VString::operator=(const CFStringRef& s) { 00328 ASSERT_INVARIANT(); 00329 00330 this->_assignFromCFString(s); 00331 00332 ASSERT_INVARIANT(); 00333 00334 return *this; 00335 } 00336 #endif /* VAULT_CORE_FOUNDATION_SUPPORT */ 00337 00338 VString& VString::operator=(const VCodePoint& cp) { 00339 ASSERT_INVARIANT(); 00340 00341 (*this) = cp.toString(); 00342 00343 ASSERT_INVARIANT(); 00344 00345 return *this; 00346 } 00347 00348 VString& VString::operator=(const VChar& c) { 00349 ASSERT_INVARIANT(); 00350 00351 this->preflight(1); 00352 _set()[0] = c.charValue(); 00353 this->_setLength(1); 00354 00355 ASSERT_INVARIANT(); 00356 00357 return *this; 00358 } 00359 00360 VString& VString::operator=(char c) { 00361 ASSERT_INVARIANT(); 00362 00363 this->preflight(1); 00364 _set()[0] = c; 00365 this->_setLength(1); 00366 00367 ASSERT_INVARIANT(); 00368 00369 return *this; 00370 } 00371 00372 VString& VString::operator=(const char* s) { 00373 ASSERT_INVARIANT(); 00374 00375 if (s == NULL) { 00376 this->_setLength(0); 00377 } else { 00378 int theLength = static_cast<int>(::strlen(s)); 00379 00380 if (theLength != 0) { 00381 this->preflight(theLength); 00382 ::memcpy(_set(), s, static_cast<VSizeType>(theLength)); // faster than strcpy? 00383 } 00384 00385 this->_setLength(theLength); 00386 } 00387 00388 ASSERT_INVARIANT(); 00389 00390 return *this; 00391 } 00392 00393 VString& VString::operator=(const std::wstring& ws) { 00394 ASSERT_INVARIANT(); 00395 00396 this->_assignFromUTF16WideString(ws); 00397 00398 ASSERT_INVARIANT(); 00399 00400 return *this; 00401 } 00402 00403 VString& VString::operator=(int i) { 00404 ASSERT_INVARIANT(); 00405 00406 this->format(VSTRING_FORMATTER_INT, i); 00407 00408 ASSERT_INVARIANT(); 00409 00410 return *this; 00411 } 00412 00413 VString& VString::operator=(Vu8 i) { 00414 ASSERT_INVARIANT(); 00415 00416 this->format(VSTRING_FORMATTER_U8, (int) i); 00417 00418 ASSERT_INVARIANT(); 00419 00420 return *this; 00421 } 00422 00423 VString& VString::operator=(Vs8 i) { 00424 ASSERT_INVARIANT(); 00425 00426 this->format(VSTRING_FORMATTER_S8, (int) i); 00427 00428 ASSERT_INVARIANT(); 00429 00430 return *this; 00431 } 00432 00433 VString& VString::operator=(Vu16 i) { 00434 ASSERT_INVARIANT(); 00435 00436 this->format(VSTRING_FORMATTER_U16, i); 00437 00438 ASSERT_INVARIANT(); 00439 00440 return *this; 00441 } 00442 00443 VString& VString::operator=(Vs16 i) { 00444 ASSERT_INVARIANT(); 00445 00446 this->format(VSTRING_FORMATTER_S16, i); 00447 00448 ASSERT_INVARIANT(); 00449 00450 return *this; 00451 } 00452 00453 VString& VString::operator=(Vu32 i) { 00454 ASSERT_INVARIANT(); 00455 00456 this->format(VSTRING_FORMATTER_U32, i); 00457 00458 ASSERT_INVARIANT(); 00459 00460 return *this; 00461 } 00462 00463 #ifndef Vx32_IS_xINT /* don't redefine if types are same */ 00464 VString& VString::operator=(Vs32 i) { 00465 ASSERT_INVARIANT(); 00466 00467 this->format(VSTRING_FORMATTER_S32, i); 00468 00469 ASSERT_INVARIANT(); 00470 00471 return *this; 00472 } 00473 #endif /* not Vx32_IS_xINT */ 00474 00475 VString& VString::operator=(Vu64 i) { 00476 ASSERT_INVARIANT(); 00477 00478 this->format(VSTRING_FORMATTER_U64, i); 00479 00480 ASSERT_INVARIANT(); 00481 00482 return *this; 00483 } 00484 00485 #ifndef Vx64_IS_xINT /* don't redefine if types are same */ 00486 VString& VString::operator=(Vs64 i) { 00487 ASSERT_INVARIANT(); 00488 00489 this->format(VSTRING_FORMATTER_S64, i); 00490 00491 ASSERT_INVARIANT(); 00492 00493 return *this; 00494 } 00495 #endif /* not Vx64_IS_xINT */ 00496 00497 VString& VString::operator=(VDouble f) { 00498 ASSERT_INVARIANT(); 00499 00500 this->format(VSTRING_FORMATTER_DOUBLE, f); 00501 00502 ASSERT_INVARIANT(); 00503 00504 return *this; 00505 } 00506 00507 VString VString::operator+(const char c) const { 00508 VString newString(VSTRING_ARGS("%s%c", this->chars(), c)); 00509 return newString; 00510 } 00511 00512 VString VString::operator+(const char* s) const { 00513 VString newString(VSTRING_ARGS("%s%s", this->chars(), s)); 00514 return newString; 00515 } 00516 00517 VString VString::operator+(const std::wstring& ws) const { 00518 VString newString(*this); 00519 newString += VString(ws); 00520 return newString; 00521 } 00522 00523 VString VString::operator+(const VString& s) const { 00524 VString newString(VSTRING_ARGS("%s%s", this->chars(), s.chars())); 00525 return newString; 00526 } 00527 00528 #ifdef VAULT_BOOST_STRING_FORMATTING_SUPPORT 00529 VString VString::operator+(const boost::format& fmt) const { 00530 VString newString(*this); 00531 newString += fmt; 00532 return newString; 00533 } 00534 #endif /* VAULT_BOOST_STRING_FORMATTING_SUPPORT */ 00535 00536 VString VString::operator+(const VCodePoint& cp) const { 00537 VString newString(*this); 00538 newString += cp.toString(); 00539 return newString; 00540 } 00541 00542 VString& VString::operator+=(const VChar& c) { 00543 ASSERT_INVARIANT(); 00544 00545 this->preflight(1 + mU.mI.mStringLength); 00546 _set()[mU.mI.mStringLength] = c.charValue(); 00547 this->_setLength(1 + mU.mI.mStringLength); 00548 00549 ASSERT_INVARIANT(); 00550 00551 return *this; 00552 } 00553 00554 VString& VString::operator+=(const VString& s) { 00555 ASSERT_INVARIANT(); 00556 00557 // We have to be careful copying the buffer if &s==this 00558 // because things morph under us as we work in that case. 00559 00560 int theLength = s.length(); 00561 00562 this->preflight(theLength + mU.mI.mStringLength); 00563 ::memcpy(&(_set()[mU.mI.mStringLength]), s.chars(), static_cast<VSizeType>(theLength)); 00564 this->_setLength(theLength + mU.mI.mStringLength); 00565 00566 ASSERT_INVARIANT(); 00567 00568 return *this; 00569 } 00570 00571 VString& VString::operator+=(char c) { 00572 ASSERT_INVARIANT(); 00573 00574 this->preflight(1 + mU.mI.mStringLength); 00575 _set()[mU.mI.mStringLength] = c; 00576 this->_setLength(1 + mU.mI.mStringLength); 00577 00578 ASSERT_INVARIANT(); 00579 00580 return *this; 00581 } 00582 00583 VString& VString::operator+=(const char* s) { 00584 ASSERT_INVARIANT(); 00585 00586 int theLength = (int) ::strlen(s); 00587 00588 this->preflight(theLength + mU.mI.mStringLength); 00589 ::memcpy(&(_set()[mU.mI.mStringLength]), s, static_cast<VSizeType>(theLength)); 00590 this->_setLength(theLength + mU.mI.mStringLength); 00591 00592 ASSERT_INVARIANT(); 00593 00594 return *this; 00595 } 00596 00597 VString& VString::operator+=(const std::wstring& ws) { 00598 VString appendage(ws); 00599 (*this) += appendage; 00600 00601 return *this; 00602 } 00603 00604 #ifdef VAULT_BOOST_STRING_FORMATTING_SUPPORT 00605 VString& VString::operator+=(const boost::format& fmt) { 00606 ASSERT_INVARIANT(); 00607 00608 int theLength = (int) fmt.size(); 00609 00610 this->preflight(theLength + mU.mI.mStringLength); 00611 ::memcpy(&(_set()[mU.mI.mStringLength]), fmt.str().c_str(), static_cast<VSizeType>(theLength)); 00612 this->_setLength(theLength + mU.mI.mStringLength); 00613 00614 ASSERT_INVARIANT(); 00615 00616 return *this; 00617 } 00618 #endif /* VAULT_BOOST_STRING_FORMATTING_SUPPORT */ 00619 00620 VString& VString::operator+=(const VCodePoint& cp) { 00621 ASSERT_INVARIANT(); 00622 00623 VString appendage = cp.toString(); 00624 (*this) += appendage; 00625 00626 ASSERT_INVARIANT(); 00627 00628 return *this; 00629 } 00630 00631 VString& VString::operator+=(int i) { 00632 ASSERT_INVARIANT(); 00633 00634 *this += VSTRING_INT(i); 00635 00636 ASSERT_INVARIANT(); 00637 00638 return *this; 00639 } 00640 00641 VString& VString::operator+=(Vu8 i) { 00642 ASSERT_INVARIANT(); 00643 00644 *this += VSTRING_U8((int) i); // int case req'd for some compilers 00645 00646 ASSERT_INVARIANT(); 00647 00648 return *this; 00649 } 00650 00651 VString& VString::operator+=(Vs8 i) { 00652 ASSERT_INVARIANT(); 00653 00654 *this += VSTRING_S8((int) i); // int case req'd for some compilers 00655 00656 ASSERT_INVARIANT(); 00657 00658 return *this; 00659 } 00660 00661 VString& VString::operator+=(Vu16 i) { 00662 ASSERT_INVARIANT(); 00663 00664 *this += VSTRING_U16(i); 00665 00666 ASSERT_INVARIANT(); 00667 00668 return *this; 00669 } 00670 00671 VString& VString::operator+=(Vs16 i) { 00672 ASSERT_INVARIANT(); 00673 00674 *this += VSTRING_S16(i); 00675 00676 ASSERT_INVARIANT(); 00677 00678 return *this; 00679 } 00680 00681 VString& VString::operator+=(Vu32 i) { 00682 ASSERT_INVARIANT(); 00683 00684 *this += VSTRING_U32(i); 00685 00686 ASSERT_INVARIANT(); 00687 00688 return *this; 00689 } 00690 00691 #ifndef Vx32_IS_xINT /* don't redefine if types are same */ 00692 VString& VString::operator+=(Vs32 i) { 00693 ASSERT_INVARIANT(); 00694 00695 *this += VSTRING_S32(i); 00696 00697 ASSERT_INVARIANT(); 00698 00699 return *this; 00700 } 00701 #endif /* not Vx32_IS_xINT */ 00702 00703 VString& VString::operator+=(Vu64 i) { 00704 ASSERT_INVARIANT(); 00705 00706 *this += VSTRING_U64(i); 00707 00708 ASSERT_INVARIANT(); 00709 00710 return *this; 00711 } 00712 00713 #ifndef Vx64_IS_xINT /* don't redefine if types are same */ 00714 VString& VString::operator+=(Vs64 i) { 00715 ASSERT_INVARIANT(); 00716 00717 *this += VSTRING_S64(i); 00718 00719 ASSERT_INVARIANT(); 00720 00721 return *this; 00722 } 00723 #endif /* not Vx64_IS_xINT */ 00724 00725 VString& VString::operator+=(VDouble d) { 00726 ASSERT_INVARIANT(); 00727 00728 *this += VSTRING_DOUBLE(d); 00729 00730 ASSERT_INVARIANT(); 00731 00732 return *this; 00733 } 00734 00735 void VString::readFromIStream(std::istream& in) { 00736 ASSERT_INVARIANT(); 00737 00738 *this = VString::EMPTY(); 00739 00740 this->appendFromIStream(in); 00741 00742 ASSERT_INVARIANT(); 00743 } 00744 00745 void VString::appendFromIStream(std::istream& in) { 00746 ASSERT_INVARIANT(); 00747 00748 char c; 00749 00750 in >> c; 00751 00752 while (c != '\0') { 00753 // Because preflight() expands the buffer in chunks, we no longer need the code that was here 00754 // that conditionally called preflight() in chunks, intending to avoid repeated single-character 00755 // buffer expansion & reallocation in this loop. 00756 00757 *this += c; 00758 00759 in >> c; 00760 } 00761 00762 ASSERT_INVARIANT(); 00763 } 00764 00765 VString::iterator VString::begin() { 00766 ASSERT_INVARIANT(); 00767 00768 VString::iterator result(*this, true/*is forward iterator*/); 00769 00770 ASSERT_INVARIANT(); 00771 00772 return result; 00773 } 00774 00775 VString::const_iterator VString::begin() const { 00776 ASSERT_INVARIANT(); 00777 00778 return VString::const_iterator(*this, true/*isForwardIterator*/); 00779 } 00780 00781 VString::iterator VString::end() { 00782 ASSERT_INVARIANT(); 00783 00784 VString::iterator result(*this, true/*isForwardIterator*/, true/*goToEnd*/); 00785 00786 ASSERT_INVARIANT(); 00787 00788 return result; 00789 } 00790 00791 VString::const_iterator VString::end() const { 00792 ASSERT_INVARIANT(); 00793 00794 return VString::const_iterator(*this, true/*isForwardIterator*/, true/*goToEnd*/); 00795 } 00796 00797 VString::iterator VString::rbegin() { 00798 ASSERT_INVARIANT(); 00799 00800 VString::iterator result(*this, false/*not isForwardIterator*/); 00801 00802 ASSERT_INVARIANT(); 00803 00804 return result; 00805 } 00806 00807 VString::const_iterator VString::rbegin() const { 00808 ASSERT_INVARIANT(); 00809 00810 return VString::const_iterator(*this, false/*not isForwardIterator*/); 00811 } 00812 00813 VString::iterator VString::rend() { 00814 ASSERT_INVARIANT(); 00815 00816 VString::iterator result(*this, false/*not isForwardIterator*/, true/*goToEnd*/); 00817 00818 ASSERT_INVARIANT(); 00819 00820 return result; 00821 } 00822 00823 VString::const_iterator VString::rend() const { 00824 ASSERT_INVARIANT(); 00825 00826 return VString::const_iterator(*this, false/*not isForwardIterator*/, true/*goToEnd*/); 00827 } 00828 00829 #ifdef VAULT_VARARG_STRING_FORMATTING_SUPPORT 00830 void VString::format(const char* formatText, ...) { 00831 ASSERT_INVARIANT(); 00832 00833 va_list args; 00834 va_start(args, formatText); 00835 00836 this->vaFormat(formatText, args); 00837 00838 va_end(args); 00839 00840 ASSERT_INVARIANT(); 00841 } 00842 #endif /* VAULT_VARARG_STRING_FORMATTING_SUPPORT */ 00843 00844 void VString::insert(const VCodePoint& cp, const VString::iterator& position) { 00845 this->insert(cp, position.getCurrentOffset()); 00846 } 00847 00848 void VString::insert(const VString& s, const VString::iterator& position) { 00849 this->insert(s, position.getCurrentOffset()); 00850 } 00851 00852 void VString::insert(char c, const VString::iterator& position) { 00853 this->insert(c, position.getCurrentOffset()); 00854 } 00855 00856 void VString::insert(const VCodePoint& cp, int offset) { 00857 this->insert(VString(cp), offset); 00858 } 00859 00860 void VString::insert(const VString& s, int offset) { 00861 ASSERT_INVARIANT(); 00862 00863 if (s.length() == 0) { // optimize the nothing-to-do case 00864 return; 00865 } 00866 00867 // If s happens to be 'this', we'll need a temporary copy of it. 00868 // Otherwise, our memmove() + memcpy() data would be messed up. 00869 VString tempCopy; // note that by just declaring this, we merely have a few bytes on the stack -- nothing on the heap yet 00870 const char* source = s.chars(); 00871 00872 if (this == &s) { 00873 tempCopy = s; // this will cause tempCopy to allocate a buffer to hold a copy of s (which is 'this') 00874 source = tempCopy.chars(); // we'll do our final memcpy() from the copy, not from the 'this' buffer that's split by the memmove() 00875 } 00876 00877 int addedLength = s.length(); 00878 int oldLength = this->length(); 00879 int newLength = oldLength + addedLength; 00880 00881 // constrain to guard against bad offset; perhaps an out-of-bounds exception would be better? 00882 int actualOffset = V_MIN(oldLength, V_MAX(0, offset)); 00883 int numBytesToMove = oldLength - actualOffset; 00884 00885 this->preflight(newLength); 00886 00887 // Need to use memmove here because memcpy behavior is undefined if ranges overlap. 00888 ::memmove(&(_set()[actualOffset+addedLength]), &(_get()[actualOffset]), numBytesToMove); // shift forward by s.length() byte, everything past the offset 00889 ::memcpy(&(_set()[actualOffset]), source, addedLength); 00890 00891 this->postflight(newLength); 00892 00893 ASSERT_INVARIANT(); 00894 } 00895 00896 void VString::insert(char c, int offset) { 00897 ASSERT_INVARIANT(); 00898 00899 // We could make a VString of c, and then insert it, but it seems 00900 // much more efficient to move a single char by itself. 00901 00902 int addedLength = 1; 00903 int oldLength = this->length(); 00904 int newLength = oldLength + addedLength; 00905 00906 // constrain to guard against bad offset; perhaps an out-of-bounds exception would be better? 00907 int actualOffset = V_MIN(oldLength, V_MAX(0, offset)); 00908 int numBytesToMove = oldLength - actualOffset; 00909 00910 this->preflight(newLength); 00911 00912 // Need to use memmove here because memcpy behavior is undefined if ranges overlap. 00913 ::memmove(&(_set()[actualOffset+addedLength]), &(_get()[actualOffset]), numBytesToMove); // shift forward by 1 byte, everything past the offset 00914 _set()[actualOffset] = c; 00915 00916 this->postflight(newLength); 00917 00918 ASSERT_INVARIANT(); 00919 } 00920 00921 int VString::getNumCodePoints() const { 00922 ASSERT_INVARIANT(); 00923 00924 if (mU.mI.mNumCodePoints == -1) { 00925 this->_determineNumCodePoints(); 00926 } 00927 00928 return mU.mI.mNumCodePoints; 00929 } 00930 00931 int VString::length() const { 00932 ASSERT_INVARIANT(); 00933 00934 return mU.mI.mStringLength; 00935 } 00936 00937 void VString::truncateCodePoints(int maxNumCodePoints) { 00938 ASSERT_INVARIANT(); 00939 00940 if ((maxNumCodePoints >= 0) && (this->getNumCodePoints() > maxNumCodePoints)) { 00941 VString::iterator pos(this->begin() + maxNumCodePoints); 00942 this->_setLength(pos.getCurrentOffset()); 00943 } 00944 00945 ASSERT_INVARIANT(); 00946 } 00947 00948 void VString::truncateLength(int maxLength) { 00949 ASSERT_INVARIANT(); 00950 00951 if ((maxLength >= 0) && (this->length() > maxLength)) { 00952 this->_setLength(maxLength); 00953 } 00954 00955 ASSERT_INVARIANT(); 00956 } 00957 00958 bool VString::isEmpty() const { 00959 ASSERT_INVARIANT(); 00960 00961 return mU.mI.mStringLength == 0; 00962 } 00963 00964 bool VString::isNotEmpty() const { 00965 ASSERT_INVARIANT(); 00966 00967 return mU.mI.mStringLength != 0; 00968 } 00969 00970 VChar VString::at(int i) const { 00971 ASSERT_INVARIANT(); 00972 00973 if (i > mU.mI.mStringLength) { 00974 throw VRangeException(VSTRING_FORMAT("VString::at(%d) index out of range for length %d.", i, mU.mI.mStringLength)); 00975 } else if (i == 0 && mU.mI.mStringLength == 0) { 00976 return VChar::NULL_CHAR(); 00977 } 00978 00979 return VChar(_get()[i]); 00980 } 00981 00982 VChar VString::operator[](int i) const { 00983 ASSERT_INVARIANT(); 00984 00985 if (i > mU.mI.mStringLength) { 00986 throw VRangeException(VSTRING_FORMAT("VString::operator[%d] index out of range for length %d.", i, mU.mI.mStringLength)); 00987 } else if (i == 0 && mU.mI.mStringLength == 0) { 00988 return VChar::NULL_CHAR(); 00989 } 00990 00991 return VChar(_get()[i]); 00992 } 00993 00994 char& VString::operator[](int i) { 00995 ASSERT_INVARIANT(); 00996 00997 if (i >= mU.mI.mStringLength) { 00998 throw VRangeException(VSTRING_FORMAT("VString::operator[%d] index out of range for length %d.", i, mU.mI.mStringLength)); 00999 } 01000 01001 return _set()[i]; 01002 } 01003 01004 char VString::charAt(int i) const { 01005 ASSERT_INVARIANT(); 01006 01007 if (i > mU.mI.mStringLength) { 01008 throw VRangeException(VSTRING_FORMAT("VString::charAt(%d) index out of range for length %d.", i, mU.mI.mStringLength)); 01009 } else if (i == 0 && mU.mI.mStringLength == 0) { 01010 return (char) 0; 01011 } 01012 01013 return _get()[i]; 01014 } 01015 01016 VString::operator const char*() const { 01017 ASSERT_INVARIANT(); 01018 01019 return _get(); 01020 } 01021 01022 const char* VString::chars() const { 01023 ASSERT_INVARIANT(); 01024 01025 return _get(); 01026 } 01027 01028 std::wstring VString::toUTF16() const { 01029 ASSERT_INVARIANT(); 01030 01031 std::wstring utf16WideString; 01032 01033 for (VString::const_iterator i = this->begin(); i != this->end(); ++i) { 01034 VCodePoint cp = (*i); 01035 utf16WideString += cp.toUTF16WideString(); 01036 } 01037 01038 return utf16WideString; 01039 } 01040 01041 #ifdef VAULT_QT_SUPPORT 01042 QString VString::qstring() const { 01043 ASSERT_INVARIANT(); 01044 01045 return QString::fromUtf8(this->chars(), this->length()); 01046 } 01047 #endif /* VAULT_QT_SUPPORT */ 01048 01049 #ifdef VAULT_CORE_FOUNDATION_SUPPORT 01050 CFStringRef VString::cfstring() const { 01051 ASSERT_INVARIANT(); 01052 01053 return CFStringCreateWithCString(NULL, this->chars(), kCFStringEncodingUTF8); 01054 } 01055 #endif /* VAULT_CORE_FOUNDATION_SUPPORT */ 01056 01057 bool VString::equalsIgnoreCase(const VString& s) const { 01058 ASSERT_INVARIANT(); 01059 01060 return this->equalsIgnoreCase(s.chars()); 01061 } 01062 01063 bool VString::equalsIgnoreCase(const char* s) const { 01064 ASSERT_INVARIANT(); 01065 01066 return this->compareIgnoreCase(s) == 0; 01067 } 01068 01069 int VString::compare(const VString& s) const { 01070 ASSERT_INVARIANT(); 01071 01072 return this->compare(s.chars()); 01073 } 01074 01075 int VString::compare(const char* s) const { 01076 ASSERT_INVARIANT(); 01077 01078 return ::strcmp(_get(), s); 01079 } 01080 01081 int VString::compareIgnoreCase(const VString& s) const { 01082 ASSERT_INVARIANT(); 01083 01084 return this->compareIgnoreCase(s.chars()); 01085 } 01086 01087 int VString::compareIgnoreCase(const char* s) const { 01088 ASSERT_INVARIANT(); 01089 01090 return vault::strcasecmp(_get(), s); 01091 } 01092 01093 bool VString::startsWith(const VString& s) const { 01094 ASSERT_INVARIANT(); 01095 01096 return this->regionMatches(0, s, 0, s.length()); 01097 } 01098 01099 bool VString::startsWithIgnoreCase(const VString& s) const { 01100 ASSERT_INVARIANT(); 01101 01102 return this->regionMatches(0, s, 0, s.length(), /* caseSensitive = */ false); 01103 } 01104 01105 bool VString::startsWith(const VCodePoint& cp) const { 01106 ASSERT_INVARIANT(); 01107 01108 if (mU.mI.mStringLength < cp.getUTF8Length()) { 01109 return false; 01110 } else { 01111 return (VCodePoint(this->getDataBufferConst(), 0) == cp); 01112 } 01113 } 01114 01115 bool VString::startsWith(char aChar) const { 01116 ASSERT_INVARIANT(); 01117 01118 if (mU.mI.mStringLength == 0) { 01119 return false; 01120 } else { 01121 return (_get()[0] == aChar); 01122 } 01123 } 01124 01125 bool VString::endsWith(const VString& s) const { 01126 ASSERT_INVARIANT(); 01127 01128 return this->regionMatches(mU.mI.mStringLength - s.length(), s, 0, s.length()); 01129 } 01130 01131 bool VString::endsWithIgnoreCase(const VString& s) const { 01132 ASSERT_INVARIANT(); 01133 01134 return this->regionMatches(mU.mI.mStringLength - s.length(), s, 0, s.length(), /* caseSensitive = */ false); 01135 } 01136 01137 bool VString::endsWith(const VCodePoint& cp) const { 01138 ASSERT_INVARIANT(); 01139 01140 if (mU.mI.mStringLength < cp.getUTF8Length()) { 01141 return false; 01142 } else { 01143 VString::const_reverse_iterator ri = this->rbegin(); 01144 return (*ri == cp); 01145 } 01146 } 01147 01148 bool VString::endsWith(char aChar) const { 01149 ASSERT_INVARIANT(); 01150 01151 if (mU.mI.mStringLength == 0) { 01152 return false; 01153 } else { 01154 return (_get()[mU.mI.mStringLength - 1] == aChar); 01155 } 01156 } 01157 01158 VString::const_iterator VString::find(const VCodePoint& cp) const { 01159 ASSERT_INVARIANT(); 01160 01161 return this->find(cp, this->begin(), this->end()); 01162 } 01163 01164 VString::iterator VString::find(const VCodePoint& cp) { 01165 ASSERT_INVARIANT(); 01166 01167 VString::iterator pos = this->find(cp, this->begin(), this->end()); 01168 01169 ASSERT_INVARIANT(); 01170 01171 return pos; 01172 } 01173 01174 VString::const_iterator VString::find(const VCodePoint& cp, const VString::const_iterator& startPosition, const VString::const_iterator& endPosition) const { 01175 ASSERT_INVARIANT(); 01176 01177 for (VString::const_iterator pos = startPosition; pos != endPosition; ++pos) { 01178 if (*pos == cp) { 01179 return pos; 01180 } 01181 } 01182 01183 return this->end(); 01184 } 01185 01186 VString::iterator VString::find(const VCodePoint& cp, const VString::iterator& startPosition, const VString::iterator& endPosition) { 01187 ASSERT_INVARIANT(); 01188 01189 for (VString::iterator pos = startPosition; pos != endPosition; ++pos) { 01190 if (*pos == cp) { 01191 return pos; 01192 } 01193 } 01194 01195 return this->end(); 01196 } 01197 01198 int VString::indexOf(char c, int fromIndex) const { 01199 ASSERT_INVARIANT(); 01200 01201 if ((fromIndex >= 0) && (fromIndex < mU.mI.mStringLength)) { 01202 const char* buf = _get(); 01203 for (int i = fromIndex; i < mU.mI.mStringLength; ++i) { 01204 if (buf[i] == c) { 01205 return i; 01206 } 01207 } 01208 } 01209 01210 return -1; 01211 } 01212 01213 int VString::indexOfIgnoreCase(char c, int fromIndex) const { 01214 ASSERT_INVARIANT(); 01215 01216 if ((fromIndex >= 0) && (fromIndex < mU.mI.mStringLength)) { 01217 const char* buf = _get(); 01218 for (int i = fromIndex; i < mU.mI.mStringLength; ++i) { 01219 if (VChar::equalsIgnoreCase(buf[i], c)) { 01220 return i; 01221 } 01222 } 01223 } 01224 01225 return -1; 01226 } 01227 01228 int VString::indexOf(const VString& s, int fromIndex) const { 01229 ASSERT_INVARIANT(); 01230 01231 if (fromIndex < 0) 01232 return -1; 01233 01234 int otherLength = s.length(); 01235 01236 for (int i = fromIndex; i < mU.mI.mStringLength; ++i) { 01237 if (this->regionMatches(i, s, 0, otherLength)) { 01238 return i; 01239 } 01240 } 01241 01242 return -1; 01243 } 01244 01245 int VString::indexOfIgnoreCase(const VString& s, int fromIndex) const { 01246 ASSERT_INVARIANT(); 01247 01248 if (fromIndex < 0) { 01249 return -1; 01250 } 01251 01252 int otherLength = s.length(); 01253 01254 for (int i = fromIndex; i < mU.mI.mStringLength; ++i) { 01255 if (this->regionMatches(i, s, 0, otherLength, /* caseSensitive = */ false)) { 01256 return i; 01257 } 01258 } 01259 01260 return -1; 01261 } 01262 01263 int VString::lastIndexOf(char c, int fromIndex) const { 01264 ASSERT_INVARIANT(); 01265 01266 if (fromIndex == -1) { 01267 fromIndex = mU.mI.mStringLength - 1; 01268 } 01269 01270 const char* buf = _get(); 01271 for (int i = fromIndex; i >= 0; --i) { 01272 if (buf[i] == c) { 01273 return i; 01274 } 01275 } 01276 01277 return -1; 01278 } 01279 01280 int VString::lastIndexOfIgnoreCase(char c, int fromIndex) const { 01281 ASSERT_INVARIANT(); 01282 01283 if (fromIndex == -1) { 01284 fromIndex = mU.mI.mStringLength - 1; 01285 } 01286 01287 const char* buf = _get(); 01288 for (int i = fromIndex; i >= 0; --i) { 01289 if (VChar::equalsIgnoreCase(buf[i], c)) { 01290 return i; 01291 } 01292 } 01293 01294 return -1; 01295 } 01296 01297 int VString::lastIndexOf(const VString& s, int fromIndex) const { 01298 ASSERT_INVARIANT(); 01299 01300 int otherLength = s.length(); 01301 01302 if (fromIndex == -1) { 01303 fromIndex = mU.mI.mStringLength; 01304 } 01305 01306 for (int i = fromIndex; i >= 0; --i) { 01307 if (this->regionMatches(i, s, 0, otherLength)) { 01308 return i; 01309 } 01310 } 01311 01312 return -1; 01313 } 01314 01315 int VString::lastIndexOfIgnoreCase(const VString& s, int fromIndex) const { 01316 ASSERT_INVARIANT(); 01317 01318 int otherLength = s.length(); 01319 01320 if (fromIndex == -1) { 01321 fromIndex = mU.mI.mStringLength; 01322 } 01323 01324 for (int i = fromIndex; i >= 0; --i) { 01325 if (this->regionMatches(i, s, 0, otherLength, /* caseSensitive = */ false)) { 01326 return i; 01327 } 01328 } 01329 01330 return -1; 01331 } 01332 01333 bool VString::regionMatches(int thisOffset, const VString& otherString, int otherOffset, int regionLength, bool caseSensitive) const { 01334 ASSERT_INVARIANT(); 01335 01336 int result; 01337 int otherStringLength = otherString.length(); 01338 01339 // Buffer offset safety checks first. If they fail, return false. 01340 if ((thisOffset < 0) || 01341 (thisOffset >= mU.mI.mStringLength) || 01342 (thisOffset + regionLength > mU.mI.mStringLength) || 01343 (otherOffset < 0) || 01344 (otherOffset >= otherStringLength) || 01345 (otherOffset + regionLength > otherStringLength)) { 01346 return false; 01347 } 01348 01349 if (caseSensitive) { 01350 result = ::strncmp(this->chars() + thisOffset, otherString.chars() + otherOffset, static_cast<VSizeType>(regionLength)); 01351 } else { 01352 result = vault::strncasecmp(this->chars() + thisOffset, otherString.chars() + otherOffset, static_cast<VSizeType>(regionLength)); 01353 } 01354 01355 return (result == 0); 01356 } 01357 01358 bool VString::contains(char c, int fromIndex) const { 01359 ASSERT_INVARIANT(); 01360 01361 return this->indexOf(c, fromIndex) != -1; 01362 } 01363 01364 bool VString::containsIgnoreCase(char c, int fromIndex) const { 01365 ASSERT_INVARIANT(); 01366 01367 return this->indexOfIgnoreCase(c, fromIndex) != -1; 01368 } 01369 01370 bool VString::contains(const VString& s, int fromIndex) const { 01371 ASSERT_INVARIANT(); 01372 01373 return this->indexOf(s, fromIndex) != -1; 01374 } 01375 01376 bool VString::containsIgnoreCase(const VString& s, int fromIndex) const { 01377 ASSERT_INVARIANT(); 01378 01379 return this->indexOfIgnoreCase(s, fromIndex) != -1; 01380 } 01381 01382 int VString::replace(const VString& searchString, const VString& replacementString, bool caseSensitiveSearch) { 01383 ASSERT_INVARIANT(); 01384 01385 int searchLength = searchString.length(); 01386 01387 if (searchLength == 0) { 01388 return 0; 01389 } 01390 01391 int replacementLength = replacementString.length(); 01392 int numReplacements = 0; 01393 int currentOffset = caseSensitiveSearch ? this->indexOf(searchString) : this->indexOfIgnoreCase(searchString); 01394 01395 while ((currentOffset != -1) && (mU.mI.mStringLength != 0)) { 01396 /* 01397 The optimization trick here is that we can place a zero byte to artificially 01398 terminate the C string buffer at the found index, creating the BEFORE part 01399 as a C string. The AFTER part remains intact. And we've been supplied the 01400 MIDDLE part. We simply format these 3 pieces together to form the new 01401 string, and then reassign it back to ourself. This relieves us of having 01402 to create multiple temporary buffers; we only create 1 temporary buffer as 01403 a result of using a VString to format into. 01404 01405 We could further optimize by 01406 combining ourself with 3 memcpy calls instead of letting vsnprintf calculate 01407 the buffer length: 01408 char* buffer = new char[this->length() + replacementLength - searchLength + 1]; 01409 1. memcpy from mBuffer[0] to buffer[0] length=currentOffset 01410 2. memcpy from mBuffer[currentOffset + searchLength] to buffer[currentOffset + replacementLength] length=(this->length() - currentOffset + searchLength) 01411 3. memcpy from replacementString.mBuffer to buffer[currentOffset] length=replacementLength 01412 4. null terminate the buffer: buffer[length of buffer] = 0 01413 (order of 1, 2, 3 is important for correct copying w/o incorrect overwriting) 01414 (something like that off the top of my head) 01415 01416 And, if replacementLength <= searchLength we could just update in place. 01417 01418 FIXME: On second thought, this in-place replacement is dangerous IF we run out of memory 01419 because we've broken the invariants. The problem is that we irrevocably alter our data 01420 before we're guaranteed we'll succeed. This is very unlikely to actually happen, but it's possible. 01421 */ 01422 01423 char* buf = _set(); 01424 buf[currentOffset] = 0; // terminate the C string buffer to create the BEFORE part 01425 char* beforePart = buf; 01426 char* afterPart = &buf[currentOffset + searchLength]; 01427 VString alteredString(VSTRING_ARGS("%s%s%s", beforePart, replacementString.chars(), afterPart)); 01428 01429 // Assign the new string to ourself -- copies its buffer into ours correctly. 01430 // (Could be optimized to just swap buffers if we defined a new friend function or two.) 01431 *this = alteredString; 01432 01433 // Finally we have to move currentOffset forward past the replacement part. 01434 currentOffset += replacementLength; 01435 01436 ++numReplacements; 01437 01438 // See if there is another occurrence to replace. 01439 currentOffset = caseSensitiveSearch ? this->indexOf(searchString, currentOffset) : this->indexOfIgnoreCase(searchString, currentOffset); 01440 } 01441 01442 ASSERT_INVARIANT(); 01443 01444 return numReplacements; 01445 } 01446 01447 int VString::replace(const VCodePoint& searchChar, const VCodePoint& replacementChar, bool caseSensitiveSearch) { 01448 ASSERT_INVARIANT(); 01449 01450 int numReplacements = this->replace(searchChar.toString(), replacementChar.toString(), caseSensitiveSearch); 01451 01452 ASSERT_INVARIANT(); 01453 01454 return numReplacements; 01455 } 01456 01457 void VString::toLowerCase() { 01458 ASSERT_INVARIANT(); 01459 01460 char* buf = _set(); 01461 for (int i = 0; i < mU.mI.mStringLength; ++i) { 01462 buf[i] = static_cast<char>(::tolower(buf[i])); 01463 } 01464 01465 ASSERT_INVARIANT(); 01466 } 01467 01468 void VString::toUpperCase() { 01469 ASSERT_INVARIANT(); 01470 01471 char* buf = _set(); 01472 for (int i = 0; i < mU.mI.mStringLength; ++i) { 01473 buf[i] = static_cast<char>(::toupper(buf[i])); 01474 } 01475 01476 ASSERT_INVARIANT(); 01477 } 01478 01479 int VString::parseInt() const { 01480 ASSERT_INVARIANT(); 01481 01482 Vs64 result = this->_parseSignedInteger(); 01483 Vs64 maxValue = V_MAX_S32; 01484 Vs64 minValue = V_MIN_S32; 01485 01486 if (sizeof(int) == 1) { 01487 maxValue = V_MAX_S8; 01488 minValue = V_MIN_S8; 01489 } else if (sizeof(int) == 2) { 01490 maxValue = V_MAX_S16; 01491 minValue = V_MIN_S16; 01492 } else if (sizeof(int) == 8) { 01493 maxValue = V_MAX_S64; 01494 minValue = V_MIN_S64; 01495 } 01496 01497 if ((result < minValue) || (result > maxValue)) { 01498 throw VRangeException(VSTRING_FORMAT("VString::parseInt %s value is out of range.", _get())); 01499 } 01500 01501 return static_cast<int>(result); 01502 } 01503 01504 Vs64 VString::parseS64() const { 01505 ASSERT_INVARIANT(); 01506 01507 Vs64 result = this->_parseSignedInteger(); 01508 01509 return result; 01510 } 01511 01512 Vu64 VString::parseU64() const { 01513 ASSERT_INVARIANT(); 01514 01515 Vu64 result = this->_parseUnsignedInteger(); 01516 01517 return result; 01518 } 01519 01520 VDouble VString::parseDouble() const { 01521 ASSERT_INVARIANT(); 01522 01523 if (mU.mI.mStringLength == 0) { 01524 return 0.0; 01525 } 01526 01527 VDouble result; 01528 int n = ::sscanf(_get(), VSTRING_FORMATTER_DOUBLE, &result); 01529 if (n == 0) { 01530 throw VRangeException(VSTRING_FORMAT("VString::parseDouble '%s' is invalid format.", _get())); 01531 } 01532 01533 return result; 01534 } 01535 01536 void VString::set(int i, const VChar& c) { 01537 ASSERT_INVARIANT(); 01538 01539 if (i >= mU.mI.mStringLength) { 01540 throw VRangeException(VSTRING_FORMAT("VString::set(%d,%c) index out of range for string length %d.", i, c.charValue(), mU.mI.mStringLength)); 01541 } 01542 01543 _set()[i] = c.charValue(); 01544 01545 ASSERT_INVARIANT(); 01546 } 01547 01548 void VString::getSubstring(VString& toString, int startIndex, int endIndex) const { 01549 ASSERT_INVARIANT(); 01550 01551 int theLength = this->length(); 01552 01553 startIndex = V_MAX(0, startIndex); // prevent negative start index 01554 startIndex = V_MIN(theLength, startIndex); // prevent start past end 01555 01556 if (endIndex == -1) { // -1 means to end of string 01557 endIndex = theLength; 01558 } 01559 01560 endIndex = V_MIN(theLength, endIndex); // prevent stop past end 01561 endIndex = V_MAX(startIndex, endIndex); // prevent stop before start 01562 01563 toString.copyFromBuffer(_get(), startIndex, endIndex); 01564 } 01565 01566 void VString::getSubstring(VString& toString, VString::const_iterator rangeStart, VString::const_iterator rangeEnd) const { 01567 ASSERT_INVARIANT(); 01568 01569 this->getSubstring(toString, rangeStart.getCurrentOffset(), rangeEnd.getCurrentOffset()); 01570 } 01571 01572 void VString::substringInPlace(int startIndex, int endIndex) { 01573 ASSERT_INVARIANT(); 01574 01575 int theLength = this->length(); 01576 01577 startIndex = V_MAX(0, startIndex); // prevent negative start index 01578 startIndex = V_MIN(theLength, startIndex); // prevent start past end 01579 01580 if (endIndex == -1) { // -1 means to end of string 01581 endIndex = theLength; 01582 } 01583 01584 endIndex = V_MIN(theLength, endIndex); // prevent stop past end 01585 endIndex = V_MAX(startIndex, endIndex); // prevent stop before start 01586 01587 // Only do something if the start/stop are not the whole string. 01588 int newLength = endIndex - startIndex; 01589 if (newLength != theLength) { 01590 ::memmove(&(_set()[0]), &(_get()[startIndex]), static_cast<VSizeType>(newLength)); 01591 this->_setLength(newLength); 01592 } 01593 01594 ASSERT_INVARIANT(); 01595 } 01596 01597 void VString::split(VStringVector& result, const VCodePoint& delimiter, int limit, bool stripTrailingEmpties) const { 01598 ASSERT_INVARIANT(); 01599 01600 result.clear(); 01601 VString nextItem; 01602 01603 for (VString::const_iterator i = this->begin(); i != this->end(); ++i) { 01604 VCodePoint cp = (*i); 01605 if (cp == delimiter) { 01606 result.push_back(nextItem); 01607 nextItem = VString::EMPTY(); 01608 01609 if ((limit != 0) && (((int) result.size()) == limit - 1)) { 01610 // We are 1 less than the limit, so the rest of the string is the remaining item. 01611 this->getSubstring(nextItem, i + 1, this->end()); 01612 result.push_back(nextItem); 01613 nextItem = VString::EMPTY(); 01614 break; 01615 } 01616 } else { 01617 nextItem += cp; 01618 } 01619 } 01620 01621 if (nextItem.isNotEmpty()) { 01622 result.push_back(nextItem); 01623 } 01624 01625 // Strip trailing empty strings if specified. 01626 if (stripTrailingEmpties) { 01627 while (result[result.size() - 1].isEmpty()) { 01628 result.erase(result.end() - 1); 01629 } 01630 } 01631 } 01632 01633 VStringVector VString::split(const VCodePoint& delimiter, int limit, bool stripTrailingEmpties) const { 01634 ASSERT_INVARIANT(); 01635 01636 VStringVector result; 01637 this->split(result, delimiter, limit, stripTrailingEmpties); 01638 return result; 01639 } 01640 01641 void VString::trim() { 01642 ASSERT_INVARIANT(); 01643 01644 int theLength = mU.mI.mStringLength; 01645 01646 // optimize for empty string condition 01647 if (theLength == 0) { 01648 return; 01649 } 01650 01651 int indexOfFirstNonWhitespace = -1; 01652 int indexOfLastNonWhitespace = -1; 01653 01654 char* buf = _set(); 01655 01656 for (int i = 0; i < theLength; ++i) { 01657 if ((buf[i] > 0x20) && (buf[i] != 0x7F)) { 01658 indexOfFirstNonWhitespace = i; 01659 break; 01660 } 01661 } 01662 01663 if (indexOfFirstNonWhitespace != -1) { 01664 for (int i = theLength - 1; i >= 0; --i) { 01665 if ((buf[i] > 0x20) && (buf[i] != 0x7F)) { 01666 indexOfLastNonWhitespace = i; 01667 break; 01668 } 01669 } 01670 } 01671 01672 /* 01673 Case 1: all whitespace - set length to zero 01674 Case 2: no leading/trailing whitespace - nothing to do 01675 Case 3: some leanding and/or trailing whitespace - move data and change length 01676 01677 Note: we assume at this point that the buffer exists and length>0 because of prior length check 01678 */ 01679 if (indexOfFirstNonWhitespace == -1) { 01680 // all whitespace - set length to zero 01681 this->_setLength(0); 01682 } else if ((indexOfFirstNonWhitespace == 0) && (indexOfLastNonWhitespace == theLength - 1)) { 01683 // no leading/trailing whitespace - nothing to do 01684 } else if (buf != NULL) { 01685 // some leanding and/or trailing whitespace - move data and change length 01686 01687 int numBytesAfterTrimming = (indexOfLastNonWhitespace - indexOfFirstNonWhitespace) + 1; 01688 01689 ::memmove(&(buf[0]), &(buf[indexOfFirstNonWhitespace]), static_cast<VSizeType>(numBytesAfterTrimming)); 01690 01691 this->_setLength(numBytesAfterTrimming); 01692 } 01693 01694 ASSERT_INVARIANT(); 01695 } 01696 01697 void VString::copyToBuffer(char* toBuffer, int bufferSize) const { 01698 ASSERT_INVARIANT(); 01699 01700 if (toBuffer == NULL) { 01701 throw VRangeException("VString::copyToBuffer: target buffer pointer is null."); 01702 } 01703 01704 if (mU.mI.mStringLength == 0) { 01705 toBuffer[0] = VCHAR_NULL_TERMINATOR; 01706 } else if (mU.mI.mStringLength < bufferSize) { 01707 ::memcpy(toBuffer, _get(), static_cast<VSizeType>(1 + mU.mI.mStringLength)); // includes our null terminator 01708 } else { 01709 ::memcpy(toBuffer, _get(), static_cast<VSizeType>(bufferSize - 1)); // only copying the part that will fit 01710 toBuffer[bufferSize-1] = VCHAR_NULL_TERMINATOR; 01711 } 01712 } 01713 01714 void VString::copyFromBuffer(const char* fromBuffer, int startIndex, int endIndex) { 01715 ASSERT_INVARIANT(); 01716 01717 if (startIndex < 0) { 01718 throw VRangeException(VSTRING_FORMAT("VString::copyFromBuffer: out of range start index %d.", startIndex)); 01719 } 01720 01721 // We allow endIndex to be less than startIndex, and compensate for that 01722 if (endIndex < startIndex) { 01723 endIndex = startIndex; 01724 } 01725 01726 this->preflight(endIndex - startIndex); 01727 ::memcpy(_set(), fromBuffer + startIndex, static_cast<VSizeType>(endIndex - startIndex)); 01728 this->postflight(endIndex - startIndex); 01729 01730 ASSERT_INVARIANT(); 01731 } 01732 01733 void VString::copyFromCString(const char* fromBuffer) { 01734 this->copyFromBuffer(fromBuffer, 0, (int) ::strlen(fromBuffer)); 01735 } 01736 01737 void VString::copyToPascalString(char* pascalBuffer) const { 01738 ASSERT_INVARIANT(); 01739 01740 if (mU.mI.mStringLength == 0) { 01741 pascalBuffer[0] = 0; 01742 } else { 01743 Vu8 constrainedLength = static_cast<Vu8>(V_MIN(255, mU.mI.mStringLength)); 01744 01745 pascalBuffer[0] = static_cast<char>(constrainedLength); 01746 ::memcpy(&(pascalBuffer[1]), _get(), constrainedLength); 01747 } 01748 } 01749 01750 void VString::copyFromPascalString(const char* pascalBuffer) { 01751 ASSERT_INVARIANT(); 01752 01753 int theLength = static_cast<int>(static_cast<Vu8>(pascalBuffer[0])); 01754 01755 this->preflight(theLength); 01756 ::memcpy(_set(), &(pascalBuffer[1]), static_cast<VSizeType>(theLength)); 01757 this->postflight(theLength); 01758 01759 ASSERT_INVARIANT(); 01760 } 01761 01762 void VString::setFourCharacterCode(Vu32 fourCharacterCode) { 01763 ASSERT_INVARIANT(); 01764 01765 char codeChars[4]; 01766 01767 for (int i = 0; i < 4; ++i) { 01768 int bitsToShift = 8 * (3 - i); 01769 Vu8 byteValue = static_cast<Vu8>((fourCharacterCode & (static_cast<Vu32>(0x000000FF) << bitsToShift)) >> bitsToShift); 01770 codeChars[i] = byteValue; 01771 01772 if (codeChars[i] == 0) { 01773 throw VRangeException(VSTRING_FORMAT("VString::setFourCharacterCode: Code 0x%08X has a zero byte.", fourCharacterCode)); 01774 } 01775 } 01776 01777 this->copyFromBuffer(codeChars, 0, 4); 01778 01779 ASSERT_INVARIANT(); 01780 } 01781 01782 Vu32 VString::getFourCharacterCode() const { 01783 ASSERT_INVARIANT(); 01784 01785 Vu32 result = 0; 01786 01787 const char* buf = _get(); 01788 for (int i = 0; i < 4; ++i) { 01789 Vu8 byteValue; 01790 01791 if (mU.mI.mStringLength > i) { 01792 byteValue = static_cast<Vu8>(buf[i]); 01793 } else { 01794 byteValue = static_cast<Vu8>(' '); 01795 } 01796 01797 int bitsToShift = 8 * (3 - i); 01798 Vu32 orValue = (byteValue << bitsToShift) & (0x000000FF << bitsToShift); 01799 01800 result |= orValue; 01801 } 01802 01803 return result; 01804 } 01805 01806 static const int HEAP_BUFFER_EXPANSION_CHUNK_SIZE = 32; 01807 01808 void VString::preflight(int stringLength) { 01809 ASSERT_INVARIANT(); 01810 01811 if (stringLength < 0) { 01812 throw VRangeException(VSTRING_FORMAT("VString::preflight: negative length %d.", stringLength)); 01813 } 01814 01815 if (mU.mI.mUsingInternalBuffer && (stringLength < VSTRING_INTERNAL_BUFFER_SIZE)) { 01816 return; // Our internal buffer is in use and it's big enough. 01817 } 01818 01819 if ((!mU.mI.mUsingInternalBuffer) && (stringLength < mU.mX.mHeapBufferLength)) { 01820 return; // Our external buffer is in use and it's big enough. 01821 } 01822 01823 // At this point, either we need to switch from internal to external, or the 01824 // external buffer is present but not big enough. So we need to allocate a new 01825 // buffer, copy the old buffer (internal or external) to it, and swap it in 01826 // or switch modes. 01827 01828 try { 01829 // Allocate the buffer in reasonable sized chunks rather than using the exact size 01830 // requested; this easily yields an order of magnitude improvement when a string is 01831 // created with multiple appends. 01832 int newBufferLength = stringLength + 1; 01833 if (HEAP_BUFFER_EXPANSION_CHUNK_SIZE != 1) { // If static analyzer complains about constant comparison, disable it in the tool. 01834 int remainder = newBufferLength % HEAP_BUFFER_EXPANSION_CHUNK_SIZE; 01835 int extra = HEAP_BUFFER_EXPANSION_CHUNK_SIZE - remainder; 01836 newBufferLength += extra; 01837 } 01838 01839 // This allocation will throw an exception if we run out of memory. Catch below. 01840 char* newBuffer = new char[newBufferLength]; 01841 01842 // Copy our old string, including the null terminator, to the new buffer. 01843 if (mU.mI.mStringLength == 0) { 01844 // If the old string length was zero, there are no characters to copy. 01845 // Just set the null terminator byte at the start of the new buffer. 01846 newBuffer[0] = '\0'; 01847 } else { 01848 // Copy our old buffer, up to and including the null terminator byte, to the new buffer. 01849 // We know that stringLength is greater than mU.mI.mStringLength, or we wouldn't be growing 01850 // the buffer in the first place. 01851 ::memcpy(newBuffer, _get(), static_cast<VSizeType>(mU.mI.mStringLength + 1)); 01852 } 01853 01854 // Bookkeeping to switch to the new buffer. 01855 // If previously using the internal buffer, this means switching modes. 01856 // If previously using a heap buffer, this means deleting the old one and swapping pointers. 01857 // We haven't changed the mU.mI.mStringLength; we've merely copied data to a larger buffer. 01858 if (mU.mI.mUsingInternalBuffer) { 01859 mU.mI.mUsingInternalBuffer = false; 01860 } else { 01861 delete [] mU.mX.mHeapBufferPtr; 01862 } 01863 01864 mU.mX.mHeapBufferPtr = newBuffer; 01865 mU.mX.mHeapBufferLength = newBufferLength; 01866 01867 } 01868 // About the exceptions we catch here: 01869 // We don't know all the possible behaviors of the exceptions that might be thrown on different platforms. 01870 // There are really three uses cases to handle, and we can handle them uniformly with the two catch blocks 01871 // below. We may catch a VException if SEH translation is installed and a low-level failure occurred. The known 01872 // case for this is a garbage (but almost good-looking) VString, whereby we memcpy() above and that fails. 01873 // The SEH will kick in, we translate it to a VException and land here. Because VException inherits from 01874 // std::exception, that is covered in the std::exception catch block. The other likely failure mode is when 01875 // we run out of memory and cannot satisfy the request to allocate the buffer via "new char[]" above. This can 01876 // also be the case if the requested length is garbage and requests a huge amount of memory that we do not have. 01877 // Since we don't know for sure whether this will be a std::exception or something else, we also need a "..." catch 01878 // block so that we can re-throw with a little extra information about where we are. In both cases we test the 01879 // invariants to make sure that our VString remains in a good state as it did on entry. 01880 catch (const std::exception& ex) { 01881 ASSERT_INVARIANT(); 01882 throw VStackTraceException(VSTRING_FORMAT("VString::preflight caught exception preflighting buffer of length %d: %s", (stringLength + 1), ex.what())); 01883 } catch (...) { 01884 ASSERT_INVARIANT(); 01885 throw VStackTraceException(VSTRING_FORMAT("VString::preflight caught exception preflighting buffer of length %d.", (stringLength + 1))); 01886 } 01887 01888 ASSERT_INVARIANT(); 01889 } 01890 01891 void VString::preflightWithSimulatedFailure() { 01892 ASSERT_INVARIANT(); 01893 01894 throw VStackTraceException("VString::preflight unable to allocate buffer. (Simulated failure)"); 01895 } 01896 01897 char* VString::buffer() { 01898 ASSERT_INVARIANT(); 01899 01900 return _set(); 01901 } 01902 01903 Vu8* VString::getDataBuffer() { 01904 ASSERT_INVARIANT(); 01905 01906 return reinterpret_cast<Vu8*>(_set()); 01907 } 01908 01909 const Vu8* VString::getDataBufferConst() const { 01910 ASSERT_INVARIANT(); 01911 01912 return reinterpret_cast<const Vu8*>(_get()); 01913 } 01914 01915 char* VString::orphanDataBuffer() { 01916 ASSERT_INVARIANT(); 01917 01918 char* orphanedBuffer = NULL; 01919 01920 if (mU.mI.mUsingInternalBuffer) { 01921 // new[] a buffer to give back, and then make our internal buffer and state empty 01922 orphanedBuffer = new char[1 + mU.mI.mStringLength]; 01923 ::memcpy(orphanedBuffer, _get(), static_cast<VSizeType>(1 + mU.mI.mStringLength)); 01924 01925 mU.mI.mInternalBuffer[0] = '\0'; 01926 mU.mI.mStringLength = 0; 01927 mU.mI.mNumCodePoints = 0; 01928 01929 } else { 01930 // hand back our heap buffer, and then switch to our internal buffer as empty 01931 orphanedBuffer = mU.mX.mHeapBufferPtr; 01932 mU.mX.mHeapBufferPtr = NULL; 01933 mU.mX.mHeapBufferLength = 0; 01934 01935 mU.mI.mUsingInternalBuffer = true; 01936 mU.mI.mInternalBuffer[0] = '\0'; 01937 mU.mI.mStringLength = 0; 01938 mU.mI.mNumCodePoints = 0; 01939 } 01940 01941 ASSERT_INVARIANT(); 01942 01943 return orphanedBuffer; 01944 } 01945 01946 void VString::postflight(int stringLength) { 01947 /* 01948 Do not assert invariant on entry -- postflight's job is to clean up 01949 after a buffer copy that presumably has made the invariant state 01950 invalid. 01951 */ 01952 01953 this->_setLength(stringLength); 01954 01955 ASSERT_INVARIANT(); 01956 } 01957 01958 #ifdef VAULT_VARARG_STRING_FORMATTING_SUPPORT 01959 void VString::vaFormat(const char* formatText, va_list args) { 01960 ASSERT_INVARIANT(); 01961 01962 if (formatText == NULL) { 01963 this->_setLength(0); 01964 } else { 01965 va_list argsCopy; 01966 va_copy(argsCopy, args); 01967 int newStringLength = VString::_determineSprintfLength(formatText, args); 01968 01969 if (newStringLength == -1) { 01970 // We were unable to determine the buffer length needed. Log an error and make the preflight 01971 // use as big a buffer as we dare: how about the size of the temporary formatting buffer. 01972 const int kTruncatedStringLength = 32768; 01973 VLOGGER_ERROR(VSTRING_FORMAT("VString: formatted string will be truncated to %d characaters.", kTruncatedStringLength)); 01974 newStringLength = kTruncatedStringLength; 01975 } 01976 01977 this->preflight(newStringLength); 01978 01979 (void) vault::vsnprintf(_set(), static_cast<VSizeType>(this->_getBufferLength()), formatText, argsCopy); 01980 01981 this->_setLength(newStringLength); // could call postflight, but would do extra assertion check 01982 } 01983 01984 ASSERT_INVARIANT(); 01985 } 01986 #endif /* VAULT_VARARG_STRING_FORMATTING_SUPPORT */ 01987 01988 void VString::_setLength(int stringLength) { 01989 if (stringLength < 0) { 01990 throw VRangeException(VSTRING_FORMAT("VString::_setLength: Out of bounds negative value %d.", stringLength)); 01991 } 01992 01993 if (stringLength >= this->_getBufferLength()) { 01994 throw VRangeException(VSTRING_FORMAT("VString::_setLength: Out of bounds value %d exceeds buffer length of %d.", stringLength, this->_getBufferLength())); 01995 } 01996 01997 if ((stringLength == 0) && !mU.mI.mUsingInternalBuffer && (mU.mX.mHeapBufferPtr != NULL)) { 01998 // String length is being set to zero and we had a heap buffer. Delete the buffer and switch to a zero length internal buffer. 01999 // Note: We could consider also switching to the internal buffer (with a copy and a heap buffer delete) if we are changing length from large to small. 02000 delete [] mU.mX.mHeapBufferPtr; 02001 mU.mX.mHeapBufferPtr = NULL; 02002 mU.mX.mHeapBufferLength = 0; 02003 mU.mI.mUsingInternalBuffer = true; 02004 mU.mI.mInternalBuffer[0] = '\0'; 02005 } else { /* */ 02006 _set()[stringLength] = '\0'; 02007 } 02008 02009 // At this point we have validated the stringLength, even if mBuffer is NULL. 02010 mU.mI.mStringLength = stringLength; 02011 mU.mI.mNumCodePoints = -1; // force recalc by next call to getNumCodePoints() if ever called 02012 } 02013 02014 Vs64 VString::_parseSignedInteger() const { 02015 Vs64 result = CONST_S64(0); 02016 Vs64 multiplier = CONST_S64(1); 02017 02018 if (mU.mI.mStringLength != 0) { 02019 // Iterate over the characters backwards, building the result. 02020 // If we encounter something illegal, throw the VRangeException. 02021 const char* buf = _get(); 02022 for (int i = mU.mI.mStringLength - 1; i >= 0; --i) { 02023 switch (buf[i]) { 02024 case '-': 02025 if (i != 0) { 02026 throw VRangeException(VSTRING_FORMAT("VString::_parseSignedInteger %c at index %d is invalid format.", buf[i], i)); 02027 } 02028 02029 result = -result; 02030 break; 02031 02032 case '+': 02033 if (i != 0) { 02034 throw VRangeException(VSTRING_FORMAT("VString::_parseSignedInteger %c at index %d is invalid format.", buf[i], i)); 02035 } 02036 break; 02037 02038 case '0': 02039 case '1': 02040 case '2': 02041 case '3': 02042 case '4': 02043 case '5': 02044 case '6': 02045 case '7': 02046 case '8': 02047 case '9': 02048 result += (multiplier * (static_cast<int>(buf[i] - '0'))); 02049 break; 02050 02051 default: 02052 throw VRangeException(VSTRING_FORMAT("VString::_parseSignedInteger %c at index %d is invalid format.", buf[i], i)); 02053 break; 02054 } 02055 02056 multiplier *= 10; 02057 } 02058 } 02059 02060 return result; 02061 } 02062 02063 Vu64 VString::_parseUnsignedInteger() const { 02064 Vu64 result = CONST_U64(0); 02065 Vs64 multiplier = CONST_S64(1); 02066 02067 if (mU.mI.mStringLength != 0) { 02068 // Iterate over the characters backwards, building the result. 02069 // If we encounter something illegal, throw the VRangeException. 02070 const char* buf = _get(); 02071 for (int i = mU.mI.mStringLength - 1; i >= 0; --i) { 02072 switch (buf[i]) { 02073 case '-': 02074 throw VRangeException(VSTRING_FORMAT("VString::_parseUnsignedInteger %c at index %d is invalid format.", buf[i], i)); 02075 break; 02076 02077 case '+': 02078 if (i != 0) { 02079 throw VRangeException(VSTRING_FORMAT("VString::_parseUnsignedInteger %c at index %d is invalid format.", buf[i], i)); 02080 } 02081 break; 02082 02083 case '0': 02084 case '1': 02085 case '2': 02086 case '3': 02087 case '4': 02088 case '5': 02089 case '6': 02090 case '7': 02091 case '8': 02092 case '9': 02093 result += (multiplier * (static_cast<int>(buf[i] - '0'))); 02094 break; 02095 02096 default: 02097 throw VRangeException(VSTRING_FORMAT("VString::_parseUnsignedInteger %c at index %d is invalid format.", buf[i], i)); 02098 break; 02099 } 02100 02101 multiplier *= 10; 02102 } 02103 } 02104 02105 return result; 02106 } 02107 02108 void VString::_assertInvariant() const { 02109 const char* buf = _get(); 02110 VASSERT_NOT_NULL(buf); 02111 VASSERT_NOT_EQUAL(buf, VCPP_DEBUG_BAD_POINTER_VALUE); 02112 VASSERT_GREATER_THAN_OR_EQUAL(mU.mI.mStringLength, 0); 02113 VASSERT_GREATER_THAN(this->_getBufferLength(), mU.mI.mStringLength); // buffer must always have room for null terminator not included in string length 02114 VASSERT(buf[mU.mI.mStringLength] == VCHAR_NULL_TERMINATOR); 02115 } 02116 02117 #ifdef VAULT_VARARG_STRING_FORMATTING_SUPPORT 02118 02119 /* 02120 The IEEE 1003.1 standard states that we can call vsnprintf with 02121 a length of 0 and a null buffer pointer, and it shall return the 02122 number of bytes that would have been written had n been sufficiently 02123 large, excluding the terminating null byte. However, this does not 02124 work with all libraries, so we set V_EFFICIENT_SPRINTF in the 02125 platform header if we can use this feature. 02126 */ 02127 #ifdef V_EFFICIENT_SPRINTF 02128 02129 // static 02130 int VString::_determineSprintfLength(const char* formatText, va_list args) { 02131 return vault::vsnprintf(NULL, 0, formatText, args); 02132 } 02133 02134 #else /* not V_EFFICIENT_SPRINTF, use inefficient method */ 02135 02136 static const int INEFFICIENT_SPRINTF_STATIC_BUFFER_SIZE = 32768; 02137 static char _inefficientSprintfBuffer[INEFFICIENT_SPRINTF_STATIC_BUFFER_SIZE]; 02138 static VMutex _inefficientSprintfBufferMutex("vsnprintf", true/*this mutex itself must not log*/); 02139 02140 // static 02141 int VString::_determineSprintfLength(const char* formatText, va_list args) { 02142 VMutexLocker locker(&_inefficientSprintfBufferMutex, "VString::_determineSprintfLength"); 02143 return vault::vsnprintf(_inefficientSprintfBuffer, INEFFICIENT_SPRINTF_STATIC_BUFFER_SIZE, formatText, args); 02144 } 02145 02146 #endif /* V_EFFICIENT_SPRINTF */ 02147 02148 #endif /* VAULT_VARARG_STRING_FORMATTING_SUPPORT */ 02149 02150 void VString::_assignFromUTF16WideString(const std::wstring& utf16WideString) { 02151 02152 *this = VString::EMPTY(); 02153 02154 int numCodeUnits = (int) utf16WideString.length(); 02155 for (int i = 0; i < numCodeUnits; ++i) { 02156 VCodePoint cp(utf16WideString, i); 02157 if (cp.getUTF16Length() == 2) { 02158 ++i; // Skip past trail surrogate we just "consumed". 02159 } 02160 (*this) += cp; 02161 } 02162 } 02163 02164 #ifdef VAULT_CORE_FOUNDATION_SUPPORT 02165 void VString::_assignFromCFString(const CFStringRef& s) { 02166 const char* cfStringBuffer = CFStringGetCStringPtr(s, kCFStringEncodingUTF8); 02167 if (cfStringBuffer != NULL) { // was able to get fast direct buffer access 02168 this->copyFromBuffer(cfStringBuffer, 0, (int) ::strlen(cfStringBuffer)); 02169 } else { 02170 bool success = false; 02171 int originalLength = static_cast<int>(CFStringGetLength(s)); 02172 int finalLength = originalLength; 02173 02174 // Because of expansion that can happen during transcoding, the number of bytes we need 02175 // might be more than the number of "characters" in the CFString. We start by trying with 02176 // a buffer of the exact size, and if that fails, we try with double the space, and finally 02177 // with quadruple the space. The only way this will fail is if the string contains 02178 // mostly non-Roman characters and thus has lots of multi-byte data. (If CFString could 02179 // tell us the output length ahead of time or upon failure, we could use that. Or, we 02180 // could use a doubling factor and keep trying until we get it.) 02181 this->preflight(finalLength); 02182 success = CFStringGetCString(s, _set(), static_cast<CFIndex>(finalLength + 1), kCFStringEncodingUTF8); 02183 02184 // try with a 2x buffer 02185 if (! success) { 02186 finalLength *= 2; 02187 this->preflight(finalLength); 02188 success = CFStringGetCString(s, _set(), static_cast<CFIndex>(finalLength + 1), kCFStringEncodingUTF8); 02189 } 02190 02191 // try with a 4x buffer 02192 if (! success) { 02193 finalLength *= 2; 02194 this->preflight(finalLength); 02195 success = CFStringGetCString(s, _set(), static_cast<CFIndex>(finalLength + 1), kCFStringEncodingUTF8); 02196 } 02197 02198 if (! success) { 02199 throw VStackTraceException(VSTRING_FORMAT("VString CFStringRef constructor allocated up to %d bytes, which was insufficient for CFStringRef of length %d.", finalLength, originalLength)); 02200 } 02201 02202 this->_setLength(finalLength); 02203 } 02204 } 02205 #endif /* VAULT_CORE_FOUNDATION_SUPPORT */ 02206 02207 void VString::_construct() { 02208 // Clearing the mX fields is just to have less garabage showing in the debugger from the get-go. 02209 // Not required, and will be defeated once the mU.mI.mInternalBuffer holds a non-empty string. 02210 mU.mX.mHeapBufferLength = 0; 02211 mU.mX.mHeapBufferPtr = NULL; 02212 02213 mU.mI.mStringLength = 0; 02214 mU.mI.mNumCodePoints = 0; 02215 mU.mI.mUsingInternalBuffer = true; 02216 mU.mI.mPadBits = 0; 02217 mU.mI.mInternalBuffer[0] = '\0'; 02218 } 02219 02220 void VString::_determineNumCodePoints() const { 02221 if (this->isEmpty()) { // optimize away need to call countUTF8CodePoints() and have it set up counting loop in the first place 02222 mU.mI.mNumCodePoints = 0; 02223 } else { 02224 mU.mI.mNumCodePoints = VCodePoint::countUTF8CodePoints(this->getDataBufferConst(), this->length()); 02225 } 02226 }