AsciiString.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483
  1. /*
  2. ** Command & Conquer Generals(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: AsciiString.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: AsciiString.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. // -----------------------------------------------------
  46. /*static*/ AsciiString AsciiString::TheEmptyString;
  47. //-----------------------------------------------------------------------------
  48. inline char* skipSeps(char* p, const char* seps)
  49. {
  50. while (*p && strchr(seps, *p) != NULL)
  51. ++p;
  52. return p;
  53. }
  54. //-----------------------------------------------------------------------------
  55. inline char* skipNonSeps(char* p, const char* seps)
  56. {
  57. while (*p && strchr(seps, *p) == NULL)
  58. ++p;
  59. return p;
  60. }
  61. //-----------------------------------------------------------------------------
  62. inline char* skipWhitespace(char* p)
  63. {
  64. while (*p && isspace(*p))
  65. ++p;
  66. return p;
  67. }
  68. //-----------------------------------------------------------------------------
  69. inline char* skipNonWhitespace(char* p)
  70. {
  71. while (*p && !isspace(*p))
  72. ++p;
  73. return p;
  74. }
  75. // -----------------------------------------------------
  76. AsciiString::AsciiString(const AsciiString& stringSrc) : m_data(stringSrc.m_data)
  77. {
  78. ScopedCriticalSection scopedCriticalSection(TheAsciiStringCriticalSection);
  79. if (m_data)
  80. ++m_data->m_refCount;
  81. validate();
  82. }
  83. // -----------------------------------------------------
  84. #ifdef _DEBUG
  85. void AsciiString::validate() const
  86. {
  87. if (!m_data) return;
  88. DEBUG_ASSERTCRASH(m_data->m_refCount > 0, ("m_refCount is zero"));
  89. DEBUG_ASSERTCRASH(m_data->m_refCount < 32000, ("m_refCount is suspiciously large"));
  90. DEBUG_ASSERTCRASH(m_data->m_numCharsAllocated > 0, ("m_numCharsAllocated is zero"));
  91. // DEBUG_ASSERTCRASH(m_data->m_numCharsAllocated < 1024, ("m_numCharsAllocated suspiciously large"));
  92. DEBUG_ASSERTCRASH(strlen(m_data->peek())+1 <= m_data->m_numCharsAllocated,("str is too long (%d) for storage",strlen(m_data->peek())+1));
  93. }
  94. #endif
  95. // -----------------------------------------------------
  96. void AsciiString::debugIgnoreLeaks()
  97. {
  98. #ifdef MEMORYPOOL_DEBUG
  99. if (m_data)
  100. {
  101. TheDynamicMemoryAllocator->debugIgnoreLeaksForThisBlock(m_data);
  102. }
  103. else
  104. {
  105. DEBUG_LOG(("cannot ignore the leak (no data)\n"));
  106. }
  107. #endif
  108. }
  109. // -----------------------------------------------------
  110. void AsciiString::ensureUniqueBufferOfSize(int numCharsNeeded, Bool preserveData, const char* strToCopy, const char* strToCat)
  111. {
  112. validate();
  113. if (m_data &&
  114. m_data->m_refCount == 1 &&
  115. m_data->m_numCharsAllocated >= numCharsNeeded)
  116. {
  117. // no buffer manhandling is needed (it's already large enough, and unique to us)
  118. if (strToCopy)
  119. strcpy(m_data->peek(), strToCopy);
  120. if (strToCat)
  121. strcat(m_data->peek(), strToCat);
  122. return;
  123. }
  124. int minBytes = sizeof(AsciiStringData) + numCharsNeeded*sizeof(char);
  125. if (minBytes > MAX_LEN)
  126. throw ERROR_OUT_OF_MEMORY;
  127. int actualBytes = TheDynamicMemoryAllocator->getActualAllocationSize(minBytes);
  128. AsciiStringData* newData = (AsciiStringData*)TheDynamicMemoryAllocator->allocateBytesDoNotZero(actualBytes, "STR_AsciiString::ensureUniqueBufferOfSize");
  129. newData->m_refCount = 1;
  130. newData->m_numCharsAllocated = (actualBytes - sizeof(AsciiStringData))/sizeof(char);
  131. #if defined(_DEBUG) || defined(_INTERNAL)
  132. newData->m_debugptr = newData->peek(); // just makes it easier to read in the debugger
  133. #endif
  134. if (m_data && preserveData)
  135. strcpy(newData->peek(), m_data->peek());
  136. else
  137. newData->peek()[0] = 0;
  138. // do these BEFORE releasing the old buffer, so that self-copies
  139. // or self-cats will work correctly.
  140. if (strToCopy)
  141. strcpy(newData->peek(), strToCopy);
  142. if (strToCat)
  143. strcat(newData->peek(), strToCat);
  144. releaseBuffer();
  145. m_data = newData;
  146. validate();
  147. }
  148. // -----------------------------------------------------
  149. void AsciiString::releaseBuffer()
  150. {
  151. ScopedCriticalSection scopedCriticalSection(TheAsciiStringCriticalSection);
  152. validate();
  153. if (m_data)
  154. {
  155. if (--m_data->m_refCount == 0)
  156. {
  157. TheDynamicMemoryAllocator->freeBytes(m_data);
  158. }
  159. m_data = 0;
  160. }
  161. validate();
  162. }
  163. // -----------------------------------------------------
  164. AsciiString::AsciiString(const char* s) : m_data(0)
  165. {
  166. //DEBUG_ASSERTCRASH(isMemoryManagerOfficiallyInited(), ("Initializing AsciiStrings prior to main (ie, as static vars) can cause memory leak reporting problems. Are you sure you want to do this?\n"));
  167. int len = (s)?strlen(s):0;
  168. if (len)
  169. {
  170. ensureUniqueBufferOfSize(len + 1, false, s, NULL);
  171. }
  172. validate();
  173. }
  174. // -----------------------------------------------------
  175. void AsciiString::set(const AsciiString& stringSrc)
  176. {
  177. ScopedCriticalSection scopedCriticalSection(TheAsciiStringCriticalSection);
  178. validate();
  179. if (&stringSrc != this)
  180. {
  181. releaseBuffer();
  182. m_data = stringSrc.m_data;
  183. if (m_data)
  184. ++m_data->m_refCount;
  185. }
  186. validate();
  187. }
  188. // -----------------------------------------------------
  189. void AsciiString::set(const char* s)
  190. {
  191. validate();
  192. if (!m_data || s != peek())
  193. {
  194. int len = s ? strlen(s) : 0;
  195. if (len)
  196. {
  197. ensureUniqueBufferOfSize(len + 1, false, s, NULL);
  198. }
  199. else
  200. {
  201. releaseBuffer();
  202. }
  203. }
  204. validate();
  205. }
  206. // -----------------------------------------------------
  207. char* AsciiString::getBufferForRead(Int len)
  208. {
  209. validate();
  210. DEBUG_ASSERTCRASH(len>0, ("No need to allocate 0 len strings."));
  211. ensureUniqueBufferOfSize(len + 1, false, NULL, NULL);
  212. validate();
  213. return peek();
  214. }
  215. // -----------------------------------------------------
  216. void AsciiString::translate(const UnicodeString& stringSrc)
  217. {
  218. validate();
  219. /// @todo srj put in a real translation here; this will only work for 7-bit ascii
  220. clear();
  221. Int len = stringSrc.getLength();
  222. for (Int i = 0; i < len; i++)
  223. concat((char)stringSrc.getCharAt(i));
  224. validate();
  225. }
  226. // -----------------------------------------------------
  227. void AsciiString::concat(const char* s)
  228. {
  229. validate();
  230. int addlen = strlen(s);
  231. if (addlen == 0)
  232. return; // my, that was easy
  233. if (m_data)
  234. {
  235. ensureUniqueBufferOfSize(getLength() + addlen + 1, true, NULL, s);
  236. }
  237. else
  238. {
  239. set(s);
  240. }
  241. validate();
  242. }
  243. // -----------------------------------------------------
  244. void AsciiString::trim()
  245. {
  246. validate();
  247. if (m_data)
  248. {
  249. char *c = peek();
  250. // Strip leading white space from the string.
  251. c = skipWhitespace(c);
  252. if (c != peek())
  253. {
  254. set(c);
  255. }
  256. if (m_data) // another check, because the previous set() could erase m_data
  257. {
  258. // Clip trailing white space from the string.
  259. int len = strlen(peek());
  260. for (int index = len-1; index >= 0; index--)
  261. {
  262. if (isspace(getCharAt(index)))
  263. {
  264. removeLastChar();
  265. }
  266. else
  267. {
  268. break;
  269. }
  270. }
  271. }
  272. }
  273. validate();
  274. }
  275. // -----------------------------------------------------
  276. void AsciiString::toLower()
  277. {
  278. validate();
  279. if (m_data)
  280. {
  281. char buf[MAX_FORMAT_BUF_LEN];
  282. strcpy(buf, peek());
  283. char *c = buf;
  284. while (c && *c)
  285. {
  286. *c = tolower(*c);
  287. c++;
  288. }
  289. set(buf);
  290. }
  291. validate();
  292. }
  293. // -----------------------------------------------------
  294. void AsciiString::removeLastChar()
  295. {
  296. validate();
  297. if (m_data)
  298. {
  299. int len = strlen(peek());
  300. if (len > 0)
  301. {
  302. ensureUniqueBufferOfSize(len+1, true, NULL, NULL);
  303. peek()[len - 1] = 0;
  304. }
  305. }
  306. validate();
  307. }
  308. // -----------------------------------------------------
  309. void AsciiString::format(AsciiString format, ...)
  310. {
  311. validate();
  312. va_list args;
  313. va_start(args, format);
  314. format_va(format, args);
  315. va_end(args);
  316. validate();
  317. }
  318. // -----------------------------------------------------
  319. void AsciiString::format(const char* format, ...)
  320. {
  321. validate();
  322. va_list args;
  323. va_start(args, format);
  324. format_va(format, args);
  325. va_end(args);
  326. validate();
  327. }
  328. // -----------------------------------------------------
  329. void AsciiString::format_va(const AsciiString& format, va_list args)
  330. {
  331. validate();
  332. char buf[MAX_FORMAT_BUF_LEN];
  333. if (_vsnprintf(buf, sizeof(buf)/sizeof(char)-1, format.str(), args) < 0)
  334. throw ERROR_OUT_OF_MEMORY;
  335. set(buf);
  336. validate();
  337. }
  338. // -----------------------------------------------------
  339. void AsciiString::format_va(const char* format, va_list args)
  340. {
  341. validate();
  342. char buf[MAX_FORMAT_BUF_LEN];
  343. if (_vsnprintf(buf, sizeof(buf)/sizeof(char)-1, format, args) < 0)
  344. throw ERROR_OUT_OF_MEMORY;
  345. set(buf);
  346. validate();
  347. }
  348. // -----------------------------------------------------
  349. Bool AsciiString::startsWith(const char* p) const
  350. {
  351. if (*p == 0)
  352. return true; // everything starts with the empty string
  353. int lenThis = getLength();
  354. int lenThat = strlen(p);
  355. if (lenThis < lenThat)
  356. return false; // that must be smaller than this
  357. return strncmp(peek(), p, lenThat) == 0;
  358. }
  359. // -----------------------------------------------------
  360. Bool AsciiString::startsWithNoCase(const char* p) const
  361. {
  362. if (*p == 0)
  363. return true; // everything starts with the empty string
  364. int lenThis = getLength();
  365. int lenThat = strlen(p);
  366. if (lenThis < lenThat)
  367. return false; // that must be smaller than this
  368. return strnicmp(peek(), p, lenThat) == 0;
  369. }
  370. // -----------------------------------------------------
  371. Bool AsciiString::endsWith(const char* p) const
  372. {
  373. if (*p == 0)
  374. return true; // everything ends with the empty string
  375. int lenThis = getLength();
  376. int lenThat = strlen(p);
  377. if (lenThis < lenThat)
  378. return false; // that must be smaller than this
  379. return strncmp(peek() + lenThis - lenThat, p, lenThat) == 0;
  380. }
  381. // -----------------------------------------------------
  382. Bool AsciiString::endsWithNoCase(const char* p) const
  383. {
  384. if (*p == 0)
  385. return true; // everything ends with the empty string
  386. int lenThis = getLength();
  387. int lenThat = strlen(p);
  388. if (lenThis < lenThat)
  389. return false; // that must be smaller than this
  390. return strnicmp(peek() + lenThis - lenThat, p, lenThat) == 0;
  391. }
  392. //-----------------------------------------------------------------------------
  393. Bool AsciiString::isNone() const
  394. {
  395. return m_data && stricmp(peek(), "None") == 0;
  396. }
  397. //-----------------------------------------------------------------------------
  398. Bool AsciiString::nextToken(AsciiString* tok, const char* seps)
  399. {
  400. if (this->isEmpty() || tok == this)
  401. return false;
  402. if (seps == NULL)
  403. seps = " \n\r\t";
  404. char* start = skipSeps(peek(), seps);
  405. char* end = skipNonSeps(start, seps);
  406. if (end > start)
  407. {
  408. Int len = end - start;
  409. char* tmp = tok->getBufferForRead(len + 1);
  410. memcpy(tmp, start, len);
  411. tmp[len] = 0;
  412. this->set(end);
  413. return true;
  414. }
  415. else
  416. {
  417. this->clear();
  418. tok->clear();
  419. return false;
  420. }
  421. }