stringBuffer.cpp 14 KB

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