stringBuffer.cc 15 KB

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