stringBuffer.cc 16 KB

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