stringBuffer.cpp 14 KB

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