stringBuffer.cpp
Engine/source/core/stringBuffer.cpp
Public Defines
define
define
define
define
Detailed Description
Public Defines
decRequestCount16()
incRequestCount16()
incRequestCount8()
SBMAddThisStringBuffer()
1 2//----------------------------------------------------------------------------- 3// Copyright (c) 2012 GarageGames, LLC 4// 5// Permission is hereby granted, free of charge, to any person obtaining a copy 6// of this software and associated documentation files (the "Software"), to 7// deal in the Software without restriction, including without limitation the 8// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 9// sell copies of the Software, and to permit persons to whom the Software is 10// furnished to do so, subject to the following conditions: 11// 12// The above copyright notice and this permission notice shall be included in 13// all copies or substantial portions of the Software. 14// 15// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 21// IN THE SOFTWARE. 22//----------------------------------------------------------------------------- 23 24#include "core/stringBuffer.h" 25#include "core/frameAllocator.h" 26#include "core/strings/unicode.h" 27#include "core/strings/stringFunctions.h" 28#include "console/engineAPI.h" 29 30 31#if defined(TORQUE_DEBUG) 32 class StringBufferManager 33 { 34 public: 35 static StringBufferManager& getManager(); 36 Vector<StringBuffer*> strings; 37 U64 request8; 38 U64 request16; 39 40 StringBufferManager() 41 { 42 request8 = 0.0; 43 request16 = 0.0; 44 VECTOR_SET_ASSOCIATION( strings ); 45 } 46 47 void add(StringBuffer* s); 48 void remove(StringBuffer* s); 49 void updateStats(); 50 void dumpStats(); 51 void dumpAllStrings(); 52 }; 53 54 DefineEngineFunction( sbmDumpStats, void, (), , "()") 55 { 56 StringBufferManager::getManager().dumpStats(); 57 } 58 59 DefineEngineFunction( sbmDumpStrings, void, (), , "()") 60 { 61 StringBufferManager::getManager().dumpAllStrings(); 62 } 63#endif // TORQUE_DEBUG 64 65 66#if defined(TORQUE_DEBUG) 67#define SBMAddThisStringBuffer() \ 68 StringBufferManager::getManager().add(this); \ 69 rc = new RequestCounts; \ 70 clearRequestCounts() 71#define incRequestCount8() rc->requestCount8++ 72#define incRequestCount16() rc->requestCount16++ 73#define decRequestCount16() rc->requestCount16-- 74#else 75#define SBMAddThisStringBuffer() 76#define incRequestCount8() 77#define incRequestCount16() 78#define decRequestCount16() 79#endif 80 81StringBuffer::StringBuffer() : mBuffer(), mBuffer8(), mDirty8(true) 82{ 83 VECTOR_SET_ASSOCIATION( mBuffer ); 84 SBMAddThisStringBuffer(); 85 mBuffer.push_back(0); 86}; 87 88/// Copy constructor. Very important. 89StringBuffer::StringBuffer(const StringBuffer ©) : mBuffer(), mBuffer8() 90{ 91 VECTOR_SET_ASSOCIATION( mBuffer ); 92 SBMAddThisStringBuffer(); 93 set(©); 94}; 95 96StringBuffer::StringBuffer(const StringBuffer *in) 97: mBuffer(), mBuffer8() 98{ 99 VECTOR_SET_ASSOCIATION( mBuffer ); 100 SBMAddThisStringBuffer(); 101 set(in); 102} 103 104StringBuffer::StringBuffer(const UTF8 *in) 105: mBuffer(), mBuffer8() 106{ 107 VECTOR_SET_ASSOCIATION( mBuffer ); 108 SBMAddThisStringBuffer(); 109 set(in); 110} 111 112StringBuffer::StringBuffer(const UTF16 *in) 113: mBuffer(), mBuffer8() 114{ 115 VECTOR_SET_ASSOCIATION( mBuffer ); 116 SBMAddThisStringBuffer(); 117 set(in); 118} 119 120//------------------------------------------------------------------------- 121 122StringBuffer::~StringBuffer() 123{ 124 // Everything will get cleared up nicely cuz it's a vector. Sweet. 125 #if defined(TORQUE_DEBUG) 126 StringBufferManager::getManager().remove(this); 127 delete rc; 128 #endif 129 130} 131 132//------------------------------------------------------------------------- 133 134void StringBuffer::set(const StringBuffer *in) 135{ 136 // Copy the vector. 137 mBuffer.setSize(in->mBuffer.size()); 138 dMemcpy(mBuffer.address(), in->mBuffer.address(), sizeof(UTF16) * mBuffer.size()); 139 mDirty8 = true; 140} 141 142void StringBuffer::set(const UTF8 *in) 143{ 144 incRequestCount8(); 145 // Convert and store. Note that a UTF16 version of the string cannot be longer. 146 FrameTemp<UTF16> tmpBuff(dStrlen(in)+1); 147 if(!in || in[0] == 0 || !convertUTF8toUTF16N(in, tmpBuff, dStrlen(in)+1)) 148 { 149 // Easy out, it's a blank string, or a bad string. 150 mBuffer.clear(); 151 mBuffer.push_back(0); 152 AssertFatal(mBuffer.last() == 0, "StringBuffer::set UTF8 - not a null terminated string!"); 153 mDirty8 = true; 154 return; 155 } 156 157 // Otherwise, we've a copy to do. (This might not be strictly necessary.) 158 mBuffer.setSize(dStrlen(tmpBuff)+1); 159 dMemcpy(mBuffer.address(), tmpBuff, sizeof(UTF16) * mBuffer.size()); 160 mBuffer.compact(); 161 AssertFatal(mBuffer.last() == 0, "StringBuffer::set UTF8 - not a null terminated string!"); 162 mDirty8 = true; 163} 164 165void StringBuffer::set(const UTF16 *in) 166{ 167 incRequestCount16(); 168 // Just copy, it's already UTF16. 169 U32 newsize = dStrlen(in); 170 mBuffer.setSize(newsize + 1); 171 dMemcpy(mBuffer.address(), in, sizeof(UTF16) * newsize); 172 mBuffer[newsize] = '\0'; 173 mBuffer.compact(); 174 AssertFatal(mBuffer.last() == 0, "StringBuffer::set UTF16 - not a null terminated string!"); 175 mDirty8 = true; 176} 177 178//------------------------------------------------------------------------- 179 180void StringBuffer::append(const StringBuffer &in) 181{ 182 append(in.mBuffer.address(), in.length()); 183} 184 185void StringBuffer::append(const UTF8* in) 186{ 187 incRequestCount8(); 188 decRequestCount16(); // because we're about to inc it when we go through append(utf16) 189 190 // convert to UTF16, because that's our internal format. 191 // if the conversion fails, exit. 192 UTF16* tmp = createUTF16string(in); 193 AssertFatal(tmp, "StringBuffer::append(UTF8) - could not convert UTF8 string!"); 194 if(!tmp) 195 return; 196 197 append(tmp); 198 delete[] tmp; 199} 200 201void StringBuffer::append(const UTF16* in) 202{ 203 AssertFatal(in, "StringBuffer::append(UTF16) - null UTF16 string!"); 204 append(in, dStrlen(in)); 205} 206 207void StringBuffer::append(const UTF16* in, const U32 len) 208{ 209 incRequestCount16(); 210 211 // Stick in onto the end of us - first make space. 212 U32 oldSize = length(); 213 mBuffer.increment(len); 214 215 // Copy it in, ignoring both our terminator and theirs. 216 dMemcpy(&mBuffer[oldSize], in, sizeof(UTF16) * len); 217 218 // Terminate the string. 219 mBuffer.last() = 0; 220 221 // mark utf8 buffer dirty 222 mDirty8 = true; 223} 224 225void StringBuffer::insert(const U32 charOffset, const StringBuffer &in) 226{ 227 insert(charOffset, in.mBuffer.address(), in.length()); 228} 229 230void StringBuffer::insert(const U32 charOffset, const UTF8* in) 231{ 232 incRequestCount8(); 233 decRequestCount16(); 234 235 // convert to UTF16, because that's our internal format. 236 // if the conversion fails, exit. 237 UTF16* tmp = createUTF16string(in); 238 AssertFatal(tmp, "StringBuffer::insert(UTF8) - could not convert UTF8 string!"); 239 if(!tmp) 240 return; 241 242 insert(charOffset, tmp); 243 delete[] tmp; 244} 245 246void StringBuffer::insert(const U32 charOffset, const UTF16* in) 247{ 248 AssertFatal(in, "StringBuffer::insert(UTF16) - null UTF16 string!"); 249 insert(charOffset, in, dStrlen(in)); 250} 251 252void StringBuffer::insert(const U32 charOffset, const UTF16* in, const U32 len) 253{ 254 incRequestCount16(); 255 256 // Deal with append case. 257 if(charOffset >= length()) 258 { 259 append(in, len); 260 return; 261 } 262 263 // Append was easy, now we have to do some work. 264 265 // Copy everything we have that comes after charOffset past where the new 266 // string data will be. 267 268 // Figure the number of UTF16's to copy, taking into account the possibility 269 // that we may be inserting a long string into a short string. 270 271 // How many chars come after the insert point? Add 1 to deal with terminator. 272 const U32 copyCharLength = (S32)(length() - charOffset) + 1; 273 274 // Make some space in our buffer. We only need in.length() more as we 275 // will be dropping one of the two terminators present in this operation. 276 mBuffer.increment(len); 277 278 for(S32 i=copyCharLength-1; i>=0; i--) 279 mBuffer[charOffset+i+len] = mBuffer[charOffset+i]; 280 281 // Can't copy directly: memcpy behavior is undefined if src and dst overlap. 282 //dMemcpy(&mBuffer[charOffset+len], &mBuffer[charOffset], sizeof(UTF16) * copyCharLength); 283 284 // And finally copy the new data in, not including its terminator. 285 dMemcpy(&mBuffer[charOffset], in, sizeof(UTF16) * len); 286 287 // All done! 288 AssertFatal(mBuffer.last() == 0, "StringBuffer::insert - not a null terminated string!"); 289 290 // mark utf8 buffer dirty 291 mDirty8 = true; 292} 293 294StringBuffer StringBuffer::substring(const U32 start, const U32 len) const 295{ 296 // Deal with bonehead user input. 297 if(start >= length() || len == 0) 298 { 299 // Either they asked beyond the end of the string or we're null. Either 300 // way, let's give them a null string. 301 return StringBuffer(""); 302 } 303 304 AssertFatal(start < length(), "StringBuffer::substring - invalid start!"); 305 AssertFatal(start+len <= length(), "StringBuffer::substring - invalid len!"); 306 AssertFatal(len > 0, "StringBuffer::substring - len must be >= 1."); 307 308 StringBuffer tmp; 309 tmp.mBuffer.clear(); 310 for(S32 i=0; i<len; i++) 311 tmp.mBuffer.push_back(mBuffer[start+i]); 312 if(tmp.mBuffer.last() != 0) tmp.mBuffer.push_back(0); 313 314 // Make sure this shit is terminated; we might get a totally empty string. 315 if(!tmp.mBuffer.size()) 316 tmp.mBuffer.push_back(0); 317 318 return tmp; 319} 320 321UTF8* StringBuffer::createSubstring8(const U32 start, const U32 len) const 322{ 323 incRequestCount8(); 324 325 StringBuffer sub = this->substring(start, len); 326 return sub.createCopy8(); 327} 328 329void StringBuffer::cut(const U32 start, const U32 len) 330{ 331 AssertFatal(start < length(), "StringBuffer::cut - invalid start!"); 332 AssertFatal(start+len <= length(), "StringBuffer::cut - invalid len!"); 333 AssertFatal(len > 0, "StringBuffer::cut - len >= 1."); 334 335 AssertFatal(mBuffer.last() == 0, "StringBuffer::cut - not a null terminated string! (pre)"); 336 337 // Now snip things. 338 for(S32 i=start; i<mBuffer.size()-len; i++) 339 mBuffer[i] = mBuffer[i+len]; 340 mBuffer.decrement(len); 341 mBuffer.compact(); 342 343 AssertFatal(mBuffer.last() == 0, "StringBuffer::cut - not a null terminated string! (post)"); 344 345 // mark utf8 buffer dirty 346 mDirty8 = true; 347} 348 349const UTF16 StringBuffer::getChar(const U32 offset) const 350{ 351 incRequestCount16(); 352 353 // Allow them to grab the null terminator if they want. 354 AssertFatal(offset<mBuffer.size(), "StringBuffer::getChar - outside of range."); 355 356 return mBuffer[offset]; 357} 358 359//------------------------------------------------------------------------- 360 361void StringBuffer::getCopy8(UTF8 *buff, const U32 buffSize) const 362{ 363 incRequestCount8(); 364 AssertFatal(mBuffer.last() == 0, "StringBuffer::get UTF8 - not a null terminated string!"); 365 convertUTF16toUTF8N(mBuffer.address(), buff, buffSize); 366} 367 368void StringBuffer::getCopy(UTF16 *buff, const U32 buffSize) const 369{ 370 incRequestCount16(); 371 // Just copy it out. 372 AssertFatal(mBuffer.last() == 0, "StringBuffer::get UTF8 - not a null terminated string!"); 373 dMemcpy(buff, mBuffer.address(), sizeof(UTF16) * getMin(buffSize, (U32)mBuffer.size())); 374 // ensure null termination. 375 buff[buffSize-1] = NULL; 376} 377 378UTF8* StringBuffer::createCopy8() const 379{ 380 incRequestCount8(); 381 // convert will create a buffer of the appropriate size for a null terminated 382 // input string. 383 UTF8* out = createUTF8string(mBuffer.address()); 384 return out; 385} 386 387const UTF16* StringBuffer::getPtr() const 388{ 389 incRequestCount16(); 390 // get a pointer to the StringBuffer's data store. 391 // this pointer is volatile, and not thread safe. 392 // use this in situations where you can be sure that the StringBuffer will 393 // not be modified out from under you. 394 // the key here is, you avoid yet another data copy. data copy is slow on 395 // most modern hardware. 396 return mBuffer.address(); 397} 398 399const UTF8* StringBuffer::getPtr8() const 400{ 401 incRequestCount8(); 402 // get a pointer to the utf8 version of the StringBuffer's data store. 403 // if the utf8 version is dirty, update it first. 404 if(mDirty8) 405 // force const cheating. 406 const_cast<StringBuffer*>(this)->updateBuffer8(); 407 return mBuffer8.address(); 408} 409 410void StringBuffer::updateBuffer8() 411{ 412 U32 slackLen = getUTF8BufferSizeEstimate(); 413 mBuffer8.setSize(slackLen); 414 U32 len = convertUTF16toUTF8N(mBuffer.address(), mBuffer8.address(), slackLen); 415 mBuffer8.setSize(len+1); 416 mBuffer8.compact(); 417 mDirty8 = false; 418} 419 420 421#if defined(TORQUE_DEBUG) 422StringBufferManager& StringBufferManager::getManager() 423{ 424 static StringBufferManager _sbm; return _sbm; 425} 426 427void StringBufferManager::add(StringBuffer* s) 428{ 429 strings.push_back(s); 430} 431 432void StringBufferManager::remove(StringBuffer* s) 433{ 434 U32 nstrings = strings.size() - 1; 435 for(S32 i = nstrings; i >= 0; i--) 436 if(strings[i] == s) 437 strings.erase_fast(i); 438} 439 440void StringBufferManager::updateStats() 441{ 442 request8 = 0; 443 request16 = 0; 444 U32 nstrings = strings.size(); 445 for(S32 i=0; i < nstrings; i++) 446 { 447 request8 += strings[i]->rc->requestCount8; 448 request16 += strings[i]->rc->requestCount16; 449 } 450} 451 452void StringBufferManager::dumpStats() 453{ 454 updateStats(); 455 Con::printf("===== String Manager Stats ====="); 456 Con::printf(" strings: %i", strings.size()); 457 Con::printf(" utf8 requests: %Lu", request8); 458 Con::printf(" utf16 requests: %Lu", request16); 459} 460 461void StringBufferManager::dumpAllStrings() 462{ 463 U32 nstrings = strings.size(); 464 Con::printf("===== String Manager: All Strings ====="); 465 Con::printf(" utf8 | utf16 | string"); 466 for(S32 i=0; i < nstrings; i++) 467 { 468 UTF8* tmp = strings[i]->createCopy8(); 469 strings[i]->rc->requestCount8--; 470 Con::printf("%5llu %5llu \"%s\"", strings[i]->rc->requestCount8, strings[i]->rc->requestCount16, tmp); 471 delete[] tmp; 472 } 473} 474 475#endif 476