AsciiString.cpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396
  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: 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. void AsciiString::freeBytes(void)
  76. {
  77. TheDynamicMemoryAllocator->freeBytes(m_data);
  78. }
  79. // -----------------------------------------------------
  80. #ifdef _DEBUG
  81. void AsciiString::validate() const
  82. {
  83. if (!m_data) return;
  84. DEBUG_ASSERTCRASH(m_data->m_refCount > 0, ("m_refCount is zero"));
  85. DEBUG_ASSERTCRASH(m_data->m_refCount < 32000, ("m_refCount is suspiciously large"));
  86. DEBUG_ASSERTCRASH(m_data->m_numCharsAllocated > 0, ("m_numCharsAllocated is zero"));
  87. // DEBUG_ASSERTCRASH(m_data->m_numCharsAllocated < 1024, ("m_numCharsAllocated suspiciously large"));
  88. DEBUG_ASSERTCRASH(strlen(m_data->peek())+1 <= m_data->m_numCharsAllocated,("str is too long (%d) for storage",strlen(m_data->peek())+1));
  89. }
  90. #endif
  91. // -----------------------------------------------------
  92. void AsciiString::debugIgnoreLeaks()
  93. {
  94. #ifdef MEMORYPOOL_DEBUG
  95. if (m_data)
  96. {
  97. TheDynamicMemoryAllocator->debugIgnoreLeaksForThisBlock(m_data);
  98. }
  99. else
  100. {
  101. DEBUG_LOG(("cannot ignore the leak (no data)\n"));
  102. }
  103. #endif
  104. }
  105. // -----------------------------------------------------
  106. void AsciiString::ensureUniqueBufferOfSize(int numCharsNeeded, Bool preserveData, const char* strToCopy, const char* strToCat)
  107. {
  108. validate();
  109. if (m_data &&
  110. m_data->m_refCount == 1 &&
  111. m_data->m_numCharsAllocated >= numCharsNeeded)
  112. {
  113. // no buffer manhandling is needed (it's already large enough, and unique to us)
  114. if (strToCopy)
  115. strcpy(m_data->peek(), strToCopy);
  116. if (strToCat)
  117. strcat(m_data->peek(), strToCat);
  118. return;
  119. }
  120. int minBytes = sizeof(AsciiStringData) + numCharsNeeded*sizeof(char);
  121. if (minBytes > MAX_LEN)
  122. throw ERROR_OUT_OF_MEMORY;
  123. int actualBytes = TheDynamicMemoryAllocator->getActualAllocationSize(minBytes);
  124. AsciiStringData* newData = (AsciiStringData*)TheDynamicMemoryAllocator->allocateBytesDoNotZero(actualBytes, "STR_AsciiString::ensureUniqueBufferOfSize");
  125. newData->m_refCount = 1;
  126. newData->m_numCharsAllocated = (actualBytes - sizeof(AsciiStringData))/sizeof(char);
  127. #if defined(_DEBUG) || defined(_INTERNAL)
  128. newData->m_debugptr = newData->peek(); // just makes it easier to read in the debugger
  129. #endif
  130. if (m_data && preserveData)
  131. strcpy(newData->peek(), m_data->peek());
  132. else
  133. newData->peek()[0] = 0;
  134. // do these BEFORE releasing the old buffer, so that self-copies
  135. // or self-cats will work correctly.
  136. if (strToCopy)
  137. strcpy(newData->peek(), strToCopy);
  138. if (strToCat)
  139. strcat(newData->peek(), strToCat);
  140. releaseBuffer();
  141. m_data = newData;
  142. validate();
  143. }
  144. // -----------------------------------------------------
  145. char* AsciiString::getBufferForRead(Int len)
  146. {
  147. validate();
  148. DEBUG_ASSERTCRASH(len>0, ("No need to allocate 0 len strings."));
  149. ensureUniqueBufferOfSize(len + 1, false, NULL, NULL);
  150. validate();
  151. return peek();
  152. }
  153. // -----------------------------------------------------
  154. void AsciiString::translate(const UnicodeString& stringSrc)
  155. {
  156. validate();
  157. /// @todo srj put in a real translation here; this will only work for 7-bit ascii
  158. clear();
  159. Int len = stringSrc.getLength();
  160. for (Int i = 0; i < len; i++)
  161. concat((char)stringSrc.getCharAt(i));
  162. validate();
  163. }
  164. // -----------------------------------------------------
  165. void AsciiString::trim()
  166. {
  167. validate();
  168. if (m_data)
  169. {
  170. char *c = peek();
  171. // Strip leading white space from the string.
  172. c = skipWhitespace(c);
  173. if (c != peek())
  174. {
  175. set(c);
  176. }
  177. if (m_data) // another check, because the previous set() could erase m_data
  178. {
  179. // Clip trailing white space from the string.
  180. int len = strlen(peek());
  181. for (int index = len-1; index >= 0; index--)
  182. {
  183. if (isspace(getCharAt(index)))
  184. {
  185. removeLastChar();
  186. }
  187. else
  188. {
  189. break;
  190. }
  191. }
  192. }
  193. }
  194. validate();
  195. }
  196. // -----------------------------------------------------
  197. void AsciiString::toLower()
  198. {
  199. validate();
  200. if (m_data)
  201. {
  202. char buf[MAX_FORMAT_BUF_LEN];
  203. strcpy(buf, peek());
  204. char *c = buf;
  205. while (c && *c)
  206. {
  207. *c = tolower(*c);
  208. c++;
  209. }
  210. set(buf);
  211. }
  212. validate();
  213. }
  214. // -----------------------------------------------------
  215. void AsciiString::removeLastChar()
  216. {
  217. validate();
  218. if (m_data)
  219. {
  220. int len = strlen(peek());
  221. if (len > 0)
  222. {
  223. ensureUniqueBufferOfSize(len+1, true, NULL, NULL);
  224. peek()[len - 1] = 0;
  225. }
  226. }
  227. validate();
  228. }
  229. // -----------------------------------------------------
  230. void AsciiString::format(AsciiString format, ...)
  231. {
  232. validate();
  233. va_list args;
  234. va_start(args, format);
  235. format_va(format, args);
  236. va_end(args);
  237. validate();
  238. }
  239. // -----------------------------------------------------
  240. void AsciiString::format(const char* format, ...)
  241. {
  242. validate();
  243. va_list args;
  244. va_start(args, format);
  245. format_va(format, args);
  246. va_end(args);
  247. validate();
  248. }
  249. // -----------------------------------------------------
  250. void AsciiString::format_va(const AsciiString& format, va_list args)
  251. {
  252. validate();
  253. char buf[MAX_FORMAT_BUF_LEN];
  254. if (_vsnprintf(buf, sizeof(buf)/sizeof(char)-1, format.str(), args) < 0)
  255. throw ERROR_OUT_OF_MEMORY;
  256. set(buf);
  257. validate();
  258. }
  259. // -----------------------------------------------------
  260. void AsciiString::format_va(const char* format, va_list args)
  261. {
  262. validate();
  263. char buf[MAX_FORMAT_BUF_LEN];
  264. if (_vsnprintf(buf, sizeof(buf)/sizeof(char)-1, format, args) < 0)
  265. throw ERROR_OUT_OF_MEMORY;
  266. set(buf);
  267. validate();
  268. }
  269. // -----------------------------------------------------
  270. Bool AsciiString::startsWith(const char* p) const
  271. {
  272. if (*p == 0)
  273. return true; // everything starts with the empty string
  274. int lenThis = getLength();
  275. int lenThat = strlen(p);
  276. if (lenThis < lenThat)
  277. return false; // that must be smaller than this
  278. return strncmp(peek(), p, lenThat) == 0;
  279. }
  280. // -----------------------------------------------------
  281. Bool AsciiString::startsWithNoCase(const char* p) const
  282. {
  283. if (*p == 0)
  284. return true; // everything starts with the empty string
  285. int lenThis = getLength();
  286. int lenThat = strlen(p);
  287. if (lenThis < lenThat)
  288. return false; // that must be smaller than this
  289. return strnicmp(peek(), p, lenThat) == 0;
  290. }
  291. // -----------------------------------------------------
  292. Bool AsciiString::endsWith(const char* p) const
  293. {
  294. if (*p == 0)
  295. return true; // everything ends with the empty string
  296. int lenThis = getLength();
  297. int lenThat = strlen(p);
  298. if (lenThis < lenThat)
  299. return false; // that must be smaller than this
  300. return strncmp(peek() + lenThis - lenThat, p, lenThat) == 0;
  301. }
  302. // -----------------------------------------------------
  303. Bool AsciiString::endsWithNoCase(const char* p) const
  304. {
  305. if (*p == 0)
  306. return true; // everything ends with the empty string
  307. int lenThis = getLength();
  308. int lenThat = strlen(p);
  309. if (lenThis < lenThat)
  310. return false; // that must be smaller than this
  311. return strnicmp(peek() + lenThis - lenThat, p, lenThat) == 0;
  312. }
  313. //-----------------------------------------------------------------------------
  314. Bool AsciiString::isNone() const
  315. {
  316. return m_data && stricmp(peek(), "None") == 0;
  317. }
  318. //-----------------------------------------------------------------------------
  319. Bool AsciiString::nextToken(AsciiString* tok, const char* seps)
  320. {
  321. if (this->isEmpty() || tok == this)
  322. return false;
  323. if (seps == NULL)
  324. seps = " \n\r\t";
  325. char* start = skipSeps(peek(), seps);
  326. char* end = skipNonSeps(start, seps);
  327. if (end > start)
  328. {
  329. Int len = end - start;
  330. char* tmp = tok->getBufferForRead(len + 1);
  331. memcpy(tmp, start, len);
  332. tmp[len] = 0;
  333. this->set(end);
  334. return true;
  335. }
  336. else
  337. {
  338. this->clear();
  339. tok->clear();
  340. return false;
  341. }
  342. }