UnicodeString.cpp 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363
  1. /*
  2. ** Command & Conquer Generals Zero Hour(tm)
  3. ** Copyright 2025 Electronic Arts Inc.
  4. **
  5. ** This program is free software: you can redistribute it and/or modify
  6. ** it under the terms of the GNU General Public License as published by
  7. ** the Free Software Foundation, either version 3 of the License, or
  8. ** (at your option) any later version.
  9. **
  10. ** This program is distributed in the hope that it will be useful,
  11. ** but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. ** GNU General Public License for more details.
  14. **
  15. ** You should have received a copy of the GNU General Public License
  16. ** along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. ////////////////////////////////////////////////////////////////////////////////
  19. // //
  20. // (c) 2001-2003 Electronic Arts Inc. //
  21. // //
  22. ////////////////////////////////////////////////////////////////////////////////
  23. // FILE: UnicodeString.cpp
  24. //-----------------------------------------------------------------------------
  25. //
  26. // Westwood Studios Pacific.
  27. //
  28. // Confidential Information
  29. // Copyright (C) 2001 - All Rights Reserved
  30. //
  31. //-----------------------------------------------------------------------------
  32. //
  33. // Project: RTS3
  34. //
  35. // File name: UnicodeString.cpp
  36. //
  37. // Created: Steven Johnson, October 2001
  38. //
  39. // Desc: General-purpose string classes
  40. //
  41. //-----------------------------------------------------------------------------
  42. ///////////////////////////////////////////////////////////////////////////////
  43. #include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
  44. #include "Common/CriticalSection.h"
  45. #ifdef _INTERNAL
  46. // for occasional debugging...
  47. //#pragma optimize("", off)
  48. //#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
  49. #endif
  50. // -----------------------------------------------------
  51. /*static*/ UnicodeString UnicodeString::TheEmptyString;
  52. // -----------------------------------------------------
  53. #ifdef _DEBUG
  54. void UnicodeString::validate() const
  55. {
  56. if (!m_data) return;
  57. DEBUG_ASSERTCRASH(m_data->m_refCount > 0, ("m_refCount is zero"));
  58. DEBUG_ASSERTCRASH(m_data->m_numCharsAllocated > 0, ("m_numCharsAllocated is zero"));
  59. DEBUG_ASSERTCRASH(wcslen(m_data->peek())+1 <= m_data->m_numCharsAllocated,("str is too long for storage"));
  60. }
  61. #endif
  62. // -----------------------------------------------------
  63. UnicodeString::UnicodeString(const UnicodeString& stringSrc) : m_data(stringSrc.m_data)
  64. {
  65. ScopedCriticalSection scopedCriticalSection(TheUnicodeStringCriticalSection);
  66. if (m_data)
  67. ++m_data->m_refCount;
  68. validate();
  69. }
  70. // -----------------------------------------------------
  71. void UnicodeString::ensureUniqueBufferOfSize(int numCharsNeeded, Bool preserveData, const WideChar* strToCopy, const WideChar* strToCat)
  72. {
  73. validate();
  74. if (m_data &&
  75. m_data->m_refCount == 1 &&
  76. m_data->m_numCharsAllocated >= numCharsNeeded)
  77. {
  78. // no buffer manhandling is needed (it's already large enough, and unique to us)
  79. if (strToCopy)
  80. wcscpy(m_data->peek(), strToCopy);
  81. if (strToCat)
  82. wcscat(m_data->peek(), strToCat);
  83. return;
  84. }
  85. int minBytes = sizeof(UnicodeStringData) + numCharsNeeded*sizeof(WideChar);
  86. if (minBytes > MAX_LEN)
  87. throw ERROR_OUT_OF_MEMORY;
  88. int actualBytes = TheDynamicMemoryAllocator->getActualAllocationSize(minBytes);
  89. UnicodeStringData* newData = (UnicodeStringData*)TheDynamicMemoryAllocator->allocateBytesDoNotZero(actualBytes, "STR_UnicodeString::ensureUniqueBufferOfSize");
  90. newData->m_refCount = 1;
  91. newData->m_numCharsAllocated = (actualBytes - sizeof(UnicodeStringData))/sizeof(WideChar);
  92. #if defined(_DEBUG) || defined(_INTERNAL)
  93. newData->m_debugptr = newData->peek(); // just makes it easier to read in the debugger
  94. #endif
  95. if (m_data && preserveData)
  96. wcscpy(newData->peek(), m_data->peek());
  97. else
  98. newData->peek()[0] = 0;
  99. // do these BEFORE releasing the old buffer, so that self-copies
  100. // or self-cats will work correctly.
  101. if (strToCopy)
  102. wcscpy(newData->peek(), strToCopy);
  103. if (strToCat)
  104. wcscat(newData->peek(), strToCat);
  105. releaseBuffer();
  106. m_data = newData;
  107. validate();
  108. }
  109. // -----------------------------------------------------
  110. void UnicodeString::releaseBuffer()
  111. {
  112. ScopedCriticalSection scopedCriticalSection(TheUnicodeStringCriticalSection);
  113. validate();
  114. if (m_data)
  115. {
  116. if (--m_data->m_refCount == 0)
  117. {
  118. TheDynamicMemoryAllocator->freeBytes(m_data);
  119. }
  120. m_data = 0;
  121. }
  122. }
  123. // -----------------------------------------------------
  124. UnicodeString::UnicodeString(const WideChar* s) : m_data(0)
  125. {
  126. int len = wcslen(s);
  127. if (len)
  128. {
  129. ensureUniqueBufferOfSize(len + 1, false, s, NULL);
  130. }
  131. validate();
  132. }
  133. // -----------------------------------------------------
  134. void UnicodeString::set(const UnicodeString& stringSrc)
  135. {
  136. ScopedCriticalSection scopedCriticalSection(TheUnicodeStringCriticalSection);
  137. validate();
  138. if (&stringSrc != this)
  139. {
  140. releaseBuffer();
  141. m_data = stringSrc.m_data;
  142. if (m_data)
  143. ++m_data->m_refCount;
  144. }
  145. validate();
  146. }
  147. // -----------------------------------------------------
  148. void UnicodeString::set(const WideChar* s)
  149. {
  150. validate();
  151. if (!m_data || s != peek())
  152. {
  153. int len = s ? wcslen(s) : 0;
  154. if (len)
  155. {
  156. ensureUniqueBufferOfSize(len + 1, false, s, NULL);
  157. }
  158. else
  159. {
  160. releaseBuffer();
  161. }
  162. }
  163. validate();
  164. }
  165. // -----------------------------------------------------
  166. WideChar* UnicodeString::getBufferForRead(Int len)
  167. {
  168. validate();
  169. DEBUG_ASSERTCRASH(len>0, ("No need to allocate 0 len strings."));
  170. ensureUniqueBufferOfSize(len + 1, false, NULL, NULL);
  171. validate();
  172. return peek();
  173. }
  174. // -----------------------------------------------------
  175. void UnicodeString::translate(const AsciiString& stringSrc)
  176. {
  177. validate();
  178. /// @todo srj put in a real translation here; this will only work for 7-bit ascii
  179. clear();
  180. Int len = stringSrc.getLength();
  181. for (Int i = 0; i < len; i++)
  182. concat((WideChar)stringSrc.getCharAt(i));
  183. validate();
  184. }
  185. // -----------------------------------------------------
  186. void UnicodeString::concat(const WideChar* s)
  187. {
  188. validate();
  189. int addlen = wcslen(s);
  190. if (addlen == 0)
  191. return; // my, that was easy
  192. if (m_data)
  193. {
  194. ensureUniqueBufferOfSize(getLength() + addlen + 1, true, NULL, s);
  195. }
  196. else
  197. {
  198. set(s);
  199. }
  200. validate();
  201. }
  202. // -----------------------------------------------------
  203. void UnicodeString::trim()
  204. {
  205. validate();
  206. if (m_data)
  207. {
  208. const WideChar *c = peek();
  209. // Strip leading white space from the string.
  210. while (c && iswspace(*c))
  211. {
  212. c++;
  213. }
  214. if (c != peek())
  215. {
  216. set(c);
  217. }
  218. if (m_data) // another check, because the previous set() could erase m_data
  219. {
  220. // Clip trailing white space from the string.
  221. int len = wcslen(peek());
  222. for (int index = len-1; index >= 0; index--)
  223. {
  224. if (iswspace(getCharAt(index)))
  225. {
  226. removeLastChar();
  227. }
  228. else
  229. {
  230. break;
  231. }
  232. }
  233. }
  234. }
  235. validate();
  236. }
  237. // -----------------------------------------------------
  238. void UnicodeString::removeLastChar()
  239. {
  240. validate();
  241. if (m_data)
  242. {
  243. int len = wcslen(peek());
  244. if (len > 0)
  245. {
  246. ensureUniqueBufferOfSize(len+1, true, NULL, NULL);
  247. peek()[len - 1] = 0;
  248. }
  249. }
  250. validate();
  251. }
  252. // -----------------------------------------------------
  253. void UnicodeString::format(UnicodeString format, ...)
  254. {
  255. validate();
  256. va_list args;
  257. va_start(args, format);
  258. format_va(format, args);
  259. va_end(args);
  260. validate();
  261. }
  262. // -----------------------------------------------------
  263. void UnicodeString::format(const WideChar* format, ...)
  264. {
  265. validate();
  266. va_list args;
  267. va_start(args, format);
  268. format_va(format, args);
  269. va_end(args);
  270. validate();
  271. }
  272. // -----------------------------------------------------
  273. void UnicodeString::format_va(const UnicodeString& format, va_list args)
  274. {
  275. validate();
  276. WideChar buf[MAX_FORMAT_BUF_LEN];
  277. if (_vsnwprintf(buf, sizeof(buf)/sizeof(WideChar)-1, format.str(), args) < 0)
  278. throw ERROR_OUT_OF_MEMORY;
  279. set(buf);
  280. validate();
  281. }
  282. // -----------------------------------------------------
  283. void UnicodeString::format_va(const WideChar* format, va_list args)
  284. {
  285. validate();
  286. WideChar buf[MAX_FORMAT_BUF_LEN];
  287. if (_vsnwprintf(buf, sizeof(buf)/sizeof(WideChar)-1, format, args) < 0)
  288. throw ERROR_OUT_OF_MEMORY;
  289. set(buf);
  290. validate();
  291. }
  292. //-----------------------------------------------------------------------------
  293. Bool UnicodeString::nextToken(UnicodeString* tok, UnicodeString delimiters)
  294. {
  295. if (this->isEmpty() || tok == this)
  296. return false;
  297. if (delimiters.isEmpty())
  298. delimiters = UnicodeString(L" \t\n\r");
  299. Int offset;
  300. offset = wcsspn(peek(), delimiters.str());
  301. WideChar* start = peek() + offset;
  302. offset = wcscspn(start, delimiters.str());
  303. WideChar* end = start + offset;
  304. if (end > start)
  305. {
  306. Int len = end - start;
  307. WideChar* tmp = tok->getBufferForRead(len + 1);
  308. memcpy(tmp, start, len*2);
  309. tmp[len] = 0;
  310. this->set(end);
  311. return true;
  312. }
  313. else
  314. {
  315. this->clear();
  316. tok->clear();
  317. return false;
  318. }
  319. }