2
0

stringBuffer.cpp 14 KB

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