AsciiString.h 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657
  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.h
  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.h
  36. //
  37. // Created: Steven Johnson, October 2001
  38. //
  39. // Desc: General-purpose string classes
  40. //
  41. //-----------------------------------------------------------------------------
  42. ///////////////////////////////////////////////////////////////////////////////
  43. #pragma once
  44. #ifndef ASCIISTRING_H
  45. #define ASCIISTRING_H
  46. #include <stdarg.h>
  47. #include <stdio.h>
  48. #include <string.h>
  49. #include "Lib/BaseType.h"
  50. #include "Common/Debug.h"
  51. #include "Common/Errors.h"
  52. class UnicodeString;
  53. #include "windows.h"
  54. // -----------------------------------------------------
  55. /**
  56. AsciiString is the fundamental single-byte string type used in the Generals
  57. code base, and should be preferred over all other string constructions
  58. (e.g., array of char, STL string<>, WWVegas StringClass, etc.)
  59. Of course, other string setups may be used when necessary or appropriate!
  60. AsciiString is modeled after the MFC CString class, with some minor
  61. syntactic differences to keep in line with our coding conventions.
  62. Basically, AsciiString allows you to treat a string as an intrinsic
  63. type, rather analogous to 'int' -- when passed by value, a new string
  64. is created, and modifying the new string doesn't modify the original.
  65. This is done fairly efficiently, so that no new memory allocation is done
  66. unless the string is actually modified.
  67. Naturally, AsciiString handles all memory issues, so there's no need
  68. to do anything to free memory... just allow the AsciiString's
  69. destructor to run.
  70. AsciiStrings are suitable for use as automatic, member, or static variables.
  71. */
  72. class AsciiString
  73. {
  74. private:
  75. // Note, this is a Plain Old Data Structure... don't
  76. // add a ctor/dtor, 'cuz they won't ever be called.
  77. struct AsciiStringData
  78. {
  79. #if defined(_DEBUG) || defined(_INTERNAL)
  80. const char* m_debugptr; // just makes it easier to read in the debugger
  81. #endif
  82. unsigned short m_refCount; // reference count
  83. unsigned short m_numCharsAllocated; // length of data allocated
  84. // char m_stringdata[];
  85. inline char* peek() { return (char*)(this+1); }
  86. };
  87. #ifdef _DEBUG
  88. void validate() const;
  89. #else
  90. inline void validate() const { }
  91. #endif
  92. void freeBytes(void);
  93. protected:
  94. AsciiStringData* m_data; // pointer to ref counted string data
  95. char* peek() const;
  96. void releaseBuffer();
  97. void ensureUniqueBufferOfSize(int numCharsNeeded, Bool preserveData, const char* strToCpy, const char* strToCat);
  98. public:
  99. enum
  100. {
  101. MAX_FORMAT_BUF_LEN = 2048, ///< max total len of string created by format/format_va
  102. MAX_LEN = 32767 ///< max total len of any AsciiString, in chars
  103. };
  104. /**
  105. This is a convenient global used to indicate the empty
  106. string, so we don't need to construct temporaries
  107. for such a common thing.
  108. */
  109. static AsciiString TheEmptyString;
  110. /**
  111. Default constructor -- construct a new, empty AsciiString.
  112. */
  113. AsciiString();
  114. /**
  115. Copy constructor -- make this AsciiString identical to the
  116. other AsciiString. (This is actually quite efficient, because
  117. they will simply share the same string and increment the
  118. refcount.)
  119. */
  120. AsciiString(const AsciiString& stringSrc);
  121. /**
  122. Constructor -- from a literal string. Constructs an AsciiString
  123. with the given string. Note that a copy of the string is made;
  124. the input ptr is not saved.
  125. Note that this is no longer explicit, as the conversion is almost
  126. always wanted, anyhow.
  127. */
  128. AsciiString(const char* s);
  129. /**
  130. Destructor. Not too exciting... clean up the works and such.
  131. */
  132. ~AsciiString();
  133. /**
  134. Return the length, in characters (not bytes!), of the string.
  135. */
  136. int getLength() const;
  137. /**
  138. Return true iff the length of the string is zero. Equivalent
  139. to (getLength() == 0) but slightly more efficient.
  140. */
  141. Bool isEmpty() const;
  142. /**
  143. Make the string empty. Equivalent to (str = "") but slightly more efficient.
  144. */
  145. void clear();
  146. /**
  147. Return the character and the given (zero-based) index into the string.
  148. No range checking is done (except in debug mode).
  149. */
  150. char getCharAt(int index) const;
  151. /**
  152. Return a pointer to the (null-terminated) string. Note that this is
  153. a const pointer: do NOT change this! It is imperative that it be
  154. impossible (or at least, really difficuly) for someone to change our
  155. private data, since it might be shared amongst other AsciiStrings.
  156. */
  157. const char* str() const;
  158. /**
  159. Makes sure there is room for a string of len+1 characters, and
  160. returns a pointer to the string buffer. This ensures that the
  161. string buffer is NOT shared. This is intended for the file reader,
  162. that is reading new strings in from a file. jba.
  163. */
  164. char* getBufferForRead(Int len);
  165. /**
  166. Replace the contents of self with the given string.
  167. (This is actually quite efficient, because
  168. they will simply share the same string and increment the
  169. refcount.)
  170. */
  171. void set(const AsciiString& stringSrc);
  172. /**
  173. Replace the contents of self with the given string.
  174. Note that a copy of the string is made; the input ptr is not saved.
  175. */
  176. void set(const char* s);
  177. /**
  178. replace contents of self with the given string. Note the
  179. nomenclature is translate rather than set; this is because
  180. not all single-byte strings translate one-for-one into
  181. UnicodeStrings, so some data manipulation may be necessary,
  182. and the resulting strings may not be equivalent.
  183. */
  184. void translate(const UnicodeString& stringSrc);
  185. /**
  186. Concatenate the given string onto self.
  187. */
  188. void concat(const AsciiString& stringSrc);
  189. /**
  190. Concatenate the given string onto self.
  191. */
  192. void concat(const char* s);
  193. /**
  194. Concatenate the given character onto self.
  195. */
  196. void concat(const char c);
  197. /**
  198. Remove leading and trailing whitespace from the string.
  199. */
  200. void trim( void );
  201. /**
  202. Make the string lowercase
  203. */
  204. void toLower( void );
  205. /**
  206. Remove the final character in the string. If the string is empty,
  207. do nothing. (This is a rather dorky method, but used a lot in
  208. text editing, thus its presence here.)
  209. */
  210. void removeLastChar();
  211. /**
  212. Analogous to sprintf() -- this formats a string according to the
  213. given sprintf-style format string (and the variable argument list)
  214. and stores the result in self.
  215. */
  216. void format(AsciiString format, ...);
  217. void format(const char* format, ...);
  218. /**
  219. Identical to format(), but takes a va_list rather than
  220. a variable argument list. (i.e., analogous to vsprintf.)
  221. */
  222. void format_va(const AsciiString& format, va_list args);
  223. void format_va(const char* format, va_list args);
  224. /**
  225. Conceptually identical to strcmp().
  226. */
  227. int compare(const AsciiString& stringSrc) const;
  228. /**
  229. Conceptually identical to strcmp().
  230. */
  231. int compare(const char* s) const;
  232. /**
  233. Conceptually identical to _stricmp().
  234. */
  235. int compareNoCase(const AsciiString& stringSrc) const;
  236. /**
  237. Conceptually identical to _stricmp().
  238. */
  239. int compareNoCase(const char* s) const;
  240. /**
  241. Conceptually identical to strchr().
  242. */
  243. const char* find(char c) const;
  244. /**
  245. Conceptually identical to strrchr().
  246. */
  247. const char* reverseFind(char c) const;
  248. /**
  249. return true iff self starts with the given string.
  250. */
  251. Bool startsWith(const char* p) const;
  252. inline Bool startsWith(const AsciiString& stringSrc) const { return startsWith(stringSrc.str()); }
  253. /**
  254. return true iff self starts with the given string. (case insensitive)
  255. */
  256. Bool startsWithNoCase(const char* p) const;
  257. inline Bool startsWithNoCase(const AsciiString& stringSrc) const { return startsWithNoCase(stringSrc.str()); }
  258. /**
  259. return true iff self ends with the given string.
  260. */
  261. Bool endsWith(const char* p) const;
  262. Bool endsWith(const AsciiString& stringSrc) const { return endsWith(stringSrc.str()); }
  263. /**
  264. return true iff self ends with the given string. (case insensitive)
  265. */
  266. Bool endsWithNoCase(const char* p) const;
  267. Bool endsWithNoCase(const AsciiString& stringSrc) const { return endsWithNoCase(stringSrc.str()); }
  268. /**
  269. conceptually similar to strtok():
  270. extract the next seps-delimited token from the front
  271. of 'this' and copy it into 'token', returning true if a nonempty
  272. token was found. (note that this modifies 'this' as well, stripping
  273. the token off!)
  274. */
  275. Bool nextToken(AsciiString* token, const char* seps = NULL);
  276. /**
  277. return true iff the string is "NONE" (case-insensitive).
  278. Hey, hokey, but we use it a ton.
  279. */
  280. Bool isNone() const;
  281. Bool isNotEmpty() const { return !isEmpty(); }
  282. Bool isNotNone() const { return !isNone(); }
  283. //
  284. // You might think it would be a good idea to overload the * operator
  285. // to allow for an implicit conversion to an char*. This is
  286. // (in theory) a good idea, but in practice, there's lots of code
  287. // that assumes it should check text fields for null, which
  288. // is meaningless for us, since we never return a null ptr.
  289. //
  290. // operator const char*() const { return str(); }
  291. //
  292. AsciiString& operator=(const AsciiString& stringSrc); ///< the same as set()
  293. AsciiString& operator=(const char* s); ///< the same as set()
  294. void debugIgnoreLeaks();
  295. };
  296. // -----------------------------------------------------
  297. inline char* AsciiString::peek() const
  298. {
  299. DEBUG_ASSERTCRASH(m_data, ("null string ptr"));
  300. validate();
  301. return m_data->peek();
  302. }
  303. // -----------------------------------------------------
  304. inline AsciiString::AsciiString() : m_data(0)
  305. {
  306. validate();
  307. }
  308. // -----------------------------------------------------
  309. inline AsciiString::AsciiString(const char* s) : m_data(0)
  310. {
  311. //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"));
  312. int len = (s)?strlen(s):0;
  313. if (len)
  314. {
  315. ensureUniqueBufferOfSize(len + 1, false, s, NULL);
  316. }
  317. validate();
  318. }
  319. // -----------------------------------------------------
  320. inline AsciiString::AsciiString(const AsciiString& stringSrc) : m_data(stringSrc.m_data)
  321. {
  322. // don't need this if we're using InterlockedIncrement
  323. // FastCriticalSectionClass::LockClass lock(TheAsciiStringCriticalSection);
  324. if (m_data)
  325. // ++m_data->m_refCount;
  326. // yes, I know it's not a DWord but we're incrementing so we're safe
  327. InterlockedIncrement((long *)&m_data->m_refCount);
  328. validate();
  329. }
  330. // -----------------------------------------------------
  331. inline void AsciiString::releaseBuffer()
  332. {
  333. // FastCriticalSectionClass::LockClass lock(TheAsciiStringCriticalSection);
  334. validate();
  335. if (m_data)
  336. {
  337. InterlockedDecrement((long *)&m_data->m_refCount);
  338. if (!m_data->m_refCount)
  339. freeBytes();
  340. m_data = 0;
  341. }
  342. validate();
  343. }
  344. // -----------------------------------------------------
  345. inline AsciiString::~AsciiString()
  346. {
  347. validate();
  348. releaseBuffer();
  349. }
  350. // -----------------------------------------------------
  351. inline int AsciiString::getLength() const
  352. {
  353. validate();
  354. return m_data ? strlen(peek()) : 0;
  355. }
  356. // -----------------------------------------------------
  357. inline Bool AsciiString::isEmpty() const
  358. {
  359. validate();
  360. return m_data == NULL || peek()[0] == 0;
  361. }
  362. // -----------------------------------------------------
  363. inline void AsciiString::clear()
  364. {
  365. validate();
  366. releaseBuffer();
  367. validate();
  368. }
  369. // -----------------------------------------------------
  370. inline char AsciiString::getCharAt(int index) const
  371. {
  372. DEBUG_ASSERTCRASH(index >= 0 && index < getLength(), ("bad index in getCharAt"));
  373. validate();
  374. return m_data ? peek()[index] : 0;
  375. }
  376. // -----------------------------------------------------
  377. inline const char* AsciiString::str() const
  378. {
  379. validate();
  380. static const char TheNullChr = 0;
  381. return m_data ? peek() : &TheNullChr;
  382. }
  383. // -----------------------------------------------------
  384. inline void AsciiString::set(const AsciiString& stringSrc)
  385. {
  386. //FastCriticalSectionClass::LockClass lock(TheAsciiStringCriticalSection);
  387. validate();
  388. if (&stringSrc != this)
  389. {
  390. // do not call releaseBuffer(); here, it locks the CS twice
  391. // from the same thread which is illegal using fast CS's
  392. if (m_data)
  393. {
  394. InterlockedDecrement((long *)&m_data->m_refCount);
  395. if (!m_data->m_refCount)
  396. freeBytes();
  397. }
  398. m_data = stringSrc.m_data;
  399. if (m_data)
  400. InterlockedIncrement((long *)&m_data->m_refCount);
  401. }
  402. validate();
  403. }
  404. // -----------------------------------------------------
  405. inline void AsciiString::set(const char* s)
  406. {
  407. validate();
  408. if (!m_data || s != peek())
  409. {
  410. int len = s ? strlen(s) : 0;
  411. if (len)
  412. {
  413. ensureUniqueBufferOfSize(len + 1, false, s, NULL);
  414. }
  415. else
  416. {
  417. releaseBuffer();
  418. }
  419. }
  420. validate();
  421. }
  422. // -----------------------------------------------------
  423. inline AsciiString& AsciiString::operator=(const AsciiString& stringSrc)
  424. {
  425. validate();
  426. set(stringSrc);
  427. validate();
  428. return *this;
  429. }
  430. // -----------------------------------------------------
  431. inline AsciiString& AsciiString::operator=(const char* s)
  432. {
  433. validate();
  434. set(s);
  435. validate();
  436. return *this;
  437. }
  438. // -----------------------------------------------------
  439. inline void AsciiString::concat(const char* s)
  440. {
  441. validate();
  442. int addlen = strlen(s);
  443. if (addlen == 0)
  444. return; // my, that was easy
  445. if (m_data)
  446. {
  447. ensureUniqueBufferOfSize(getLength() + addlen + 1, true, NULL, s);
  448. }
  449. else
  450. {
  451. set(s);
  452. }
  453. validate();
  454. }
  455. // -----------------------------------------------------
  456. inline void AsciiString::concat(const AsciiString& stringSrc)
  457. {
  458. validate();
  459. concat(stringSrc.str());
  460. validate();
  461. }
  462. // -----------------------------------------------------
  463. inline void AsciiString::concat(const char c)
  464. {
  465. validate();
  466. /// this can probably be made more efficient, if necessary
  467. char tmp[2] = { c, 0 };
  468. concat(tmp);
  469. validate();
  470. }
  471. // -----------------------------------------------------
  472. inline int AsciiString::compare(const AsciiString& stringSrc) const
  473. {
  474. validate();
  475. return strcmp(this->str(), stringSrc.str());
  476. }
  477. // -----------------------------------------------------
  478. inline int AsciiString::compare(const char* s) const
  479. {
  480. validate();
  481. return strcmp(this->str(), s);
  482. }
  483. // -----------------------------------------------------
  484. inline int AsciiString::compareNoCase(const AsciiString& stringSrc) const
  485. {
  486. validate();
  487. return _stricmp(this->str(), stringSrc.str());
  488. }
  489. // -----------------------------------------------------
  490. inline int AsciiString::compareNoCase(const char* s) const
  491. {
  492. validate();
  493. return _stricmp(this->str(), s);
  494. }
  495. // -----------------------------------------------------
  496. inline const char* AsciiString::find(char c) const
  497. {
  498. return strchr(this->str(), c);
  499. }
  500. // -----------------------------------------------------
  501. inline const char* AsciiString::reverseFind(char c) const
  502. {
  503. return strrchr(this->str(), c);
  504. }
  505. // -----------------------------------------------------
  506. inline Bool operator==(const AsciiString& s1, const AsciiString& s2)
  507. {
  508. return strcmp(s1.str(), s2.str()) == 0;
  509. }
  510. // -----------------------------------------------------
  511. inline Bool operator!=(const AsciiString& s1, const AsciiString& s2)
  512. {
  513. return strcmp(s1.str(), s2.str()) != 0;
  514. }
  515. // -----------------------------------------------------
  516. inline Bool operator<(const AsciiString& s1, const AsciiString& s2)
  517. {
  518. return strcmp(s1.str(), s2.str()) < 0;
  519. }
  520. // -----------------------------------------------------
  521. inline Bool operator<=(const AsciiString& s1, const AsciiString& s2)
  522. {
  523. return strcmp(s1.str(), s2.str()) <= 0;
  524. }
  525. // -----------------------------------------------------
  526. inline Bool operator>(const AsciiString& s1, const AsciiString& s2)
  527. {
  528. return strcmp(s1.str(), s2.str()) > 0;
  529. }
  530. // -----------------------------------------------------
  531. inline Bool operator>=(const AsciiString& s1, const AsciiString& s2)
  532. {
  533. return strcmp(s1.str(), s2.str()) >= 0;
  534. }
  535. // -----------------------------------------------------
  536. inline Bool operator==(const AsciiString& s1, const char* s2)
  537. {
  538. return strcmp(s1.str(), s2) == 0;
  539. }
  540. // -----------------------------------------------------
  541. inline Bool operator!=(const AsciiString& s1, const char* s2)
  542. {
  543. return strcmp(s1.str(), s2) != 0;
  544. }
  545. // -----------------------------------------------------
  546. inline Bool operator<(const AsciiString& s1, const char* s2)
  547. {
  548. return strcmp(s1.str(), s2) < 0;
  549. }
  550. // -----------------------------------------------------
  551. inline Bool operator<=(const AsciiString& s1, const char* s2)
  552. {
  553. return strcmp(s1.str(), s2) <= 0;
  554. }
  555. // -----------------------------------------------------
  556. inline Bool operator>(const AsciiString& s1, const char* s2)
  557. {
  558. return strcmp(s1.str(), s2) > 0;
  559. }
  560. // -----------------------------------------------------
  561. inline Bool operator>=(const AsciiString& s1, const char* s2)
  562. {
  563. return strcmp(s1.str(), s2) >= 0;
  564. }
  565. #endif // ASCIISTRING_H