String.h 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627
  1. // Copyright (C) 2009-2016, Panagiotis Christopoulos Charitos.
  2. // All rights reserved.
  3. // Code licensed under the BSD License.
  4. // http://www.anki3d.org/LICENSE
  5. #pragma once
  6. #include <anki/util/DArray.h>
  7. #include <anki/util/Array.h>
  8. #include <anki/util/NonCopyable.h>
  9. #include <cstring>
  10. #include <cstdio>
  11. namespace anki
  12. {
  13. // Forward
  14. class String;
  15. /// @addtogroup util_private
  16. /// @{
  17. namespace detail
  18. {
  19. template<typename TNumber>
  20. constexpr const char* toStringFormat()
  21. {
  22. return nullptr;
  23. }
  24. #define ANKI_DEPLOY_TO_STRING(type_, string_) \
  25. template<> \
  26. constexpr const char* toStringFormat<type_>() \
  27. { \
  28. return string_; \
  29. }
  30. ANKI_DEPLOY_TO_STRING(I8, "%d")
  31. ANKI_DEPLOY_TO_STRING(I16, "%d")
  32. ANKI_DEPLOY_TO_STRING(I32, "%d")
  33. ANKI_DEPLOY_TO_STRING(I64, "%lld")
  34. ANKI_DEPLOY_TO_STRING(U8, "%u")
  35. ANKI_DEPLOY_TO_STRING(U16, "%u")
  36. ANKI_DEPLOY_TO_STRING(U32, "%u")
  37. ANKI_DEPLOY_TO_STRING(U64, "%llu")
  38. ANKI_DEPLOY_TO_STRING(F32, "%f")
  39. ANKI_DEPLOY_TO_STRING(F64, "%f")
  40. #undef ANKI_DEPLOY_TO_STRING
  41. } // end namespace detail
  42. /// @}
  43. /// @addtogroup util_containers
  44. /// @{
  45. /// A wrapper on top of C strings. Used mainly for safety.
  46. class CString
  47. {
  48. public:
  49. using Char = char;
  50. static const PtrSize NPOS = MAX_PTR_SIZE;
  51. CString() = default;
  52. CString(const Char* ptr)
  53. : m_ptr(ptr)
  54. {
  55. checkInit();
  56. }
  57. /// Copy constructor.
  58. CString(const CString& b)
  59. : m_ptr(b.m_ptr)
  60. {
  61. checkInit();
  62. }
  63. /// Copy.
  64. CString& operator=(const CString& b)
  65. {
  66. m_ptr = b.m_ptr;
  67. return *this;
  68. }
  69. /// Return true if the string is initialized.
  70. operator Bool() const
  71. {
  72. return !isEmpty();
  73. }
  74. /// Return char at the specified position.
  75. const Char& operator[](U pos) const
  76. {
  77. checkInit();
  78. ANKI_ASSERT(pos <= getLength());
  79. return m_ptr[pos];
  80. }
  81. const Char* begin() const
  82. {
  83. checkInit();
  84. return &m_ptr[0];
  85. }
  86. const Char* end() const
  87. {
  88. checkInit();
  89. return &m_ptr[getLength()];
  90. }
  91. /// Return true if the string is not initialized.
  92. Bool isEmpty() const
  93. {
  94. return m_ptr == nullptr || getLength() == 0;
  95. }
  96. Bool operator==(const CString& b) const
  97. {
  98. if(m_ptr == nullptr || b.m_ptr == nullptr)
  99. {
  100. return m_ptr == b.m_ptr;
  101. }
  102. else
  103. {
  104. return std::strcmp(m_ptr, b.m_ptr) == 0;
  105. }
  106. }
  107. Bool operator!=(const CString& b) const
  108. {
  109. return !((*this) == b);
  110. }
  111. Bool operator<(const CString& b) const
  112. {
  113. if(m_ptr == nullptr || b.m_ptr == nullptr)
  114. {
  115. return false;
  116. }
  117. else
  118. {
  119. return std::strcmp(m_ptr, b.m_ptr) < 0;
  120. }
  121. }
  122. Bool operator<=(const CString& b) const
  123. {
  124. if(m_ptr == nullptr || b.m_ptr == nullptr)
  125. {
  126. return m_ptr == b.m_ptr;
  127. }
  128. else
  129. {
  130. return std::strcmp(m_ptr, b.m_ptr) <= 0;
  131. }
  132. }
  133. Bool operator>(const CString& b) const
  134. {
  135. if(m_ptr == nullptr || b.m_ptr == nullptr)
  136. {
  137. return false;
  138. }
  139. else
  140. {
  141. return std::strcmp(m_ptr, b.m_ptr) > 0;
  142. }
  143. }
  144. Bool operator>=(const CString& b) const
  145. {
  146. if(m_ptr == nullptr || b.m_ptr == nullptr)
  147. {
  148. return m_ptr == b.m_ptr;
  149. }
  150. else
  151. {
  152. return std::strcmp(m_ptr, b.m_ptr) >= 0;
  153. }
  154. }
  155. /// Get the underlying C string.
  156. const char* get() const
  157. {
  158. checkInit();
  159. return m_ptr;
  160. }
  161. /// Get the string length.
  162. U getLength() const
  163. {
  164. checkInit();
  165. return std::strlen(m_ptr);
  166. }
  167. PtrSize find(const CString& cstr, PtrSize position = 0) const
  168. {
  169. checkInit();
  170. ANKI_ASSERT(position < getLength());
  171. const Char* out = std::strstr(m_ptr, &cstr[0]);
  172. return (out == nullptr) ? NPOS : (out - m_ptr);
  173. }
  174. /// Convert to F64.
  175. ANKI_USE_RESULT Error toF64(F64& out) const;
  176. /// Convert to F32.
  177. ANKI_USE_RESULT Error toF32(F32& out) const;
  178. /// Convert to I64.
  179. ANKI_USE_RESULT Error toI64(I64& out) const;
  180. private:
  181. const Char* m_ptr = nullptr;
  182. void checkInit() const
  183. {
  184. ANKI_ASSERT(m_ptr != nullptr);
  185. }
  186. };
  187. /// The base class for strings.
  188. class String : public NonCopyable
  189. {
  190. public:
  191. using Char = char; ///< Character type
  192. using CStringType = CString;
  193. using Iterator = Char*;
  194. using ConstIterator = const Char*;
  195. using Allocator = GenericMemoryPoolAllocator<Char>;
  196. static const PtrSize NPOS = MAX_PTR_SIZE;
  197. /// Default constructor.
  198. String()
  199. {
  200. }
  201. /// Move constructor.
  202. String(String&& b)
  203. {
  204. move(b);
  205. }
  206. /// Requires manual destruction.
  207. ~String()
  208. {
  209. }
  210. /// Initialize using a const string.
  211. void create(Allocator alloc, const CStringType& cstr);
  212. /// Initialize using a range. Copies the range of [first, last)
  213. void create(Allocator alloc, ConstIterator first, ConstIterator last);
  214. /// Initialize using a character.
  215. void create(Allocator alloc, Char c, PtrSize length);
  216. /// Copy one string to this one.
  217. void create(Allocator alloc, const String& b)
  218. {
  219. create(alloc, b.toCString());
  220. }
  221. /// Destroy the string.
  222. void destroy(Allocator alloc)
  223. {
  224. m_data.destroy(alloc);
  225. }
  226. /// Move one string to this one.
  227. String& operator=(String&& b)
  228. {
  229. move(b);
  230. return *this;
  231. }
  232. /// Return char at the specified position.
  233. const Char& operator[](U pos) const
  234. {
  235. checkInit();
  236. return m_data[pos];
  237. }
  238. /// Return char at the specified position as a modifiable reference.
  239. Char& operator[](U pos)
  240. {
  241. checkInit();
  242. return m_data[pos];
  243. }
  244. Iterator begin()
  245. {
  246. checkInit();
  247. return &m_data[0];
  248. }
  249. ConstIterator begin() const
  250. {
  251. checkInit();
  252. return &m_data[0];
  253. }
  254. Iterator end()
  255. {
  256. checkInit();
  257. return &m_data[m_data.getSize() - 1];
  258. }
  259. ConstIterator end() const
  260. {
  261. checkInit();
  262. return &m_data[m_data.getSize() - 1];
  263. }
  264. /// Return true if strings are equal
  265. Bool operator==(const String& b) const
  266. {
  267. checkInit();
  268. b.checkInit();
  269. return std::strcmp(&m_data[0], &b.m_data[0]) == 0;
  270. }
  271. /// Return true if strings are not equal
  272. Bool operator!=(const String& b) const
  273. {
  274. return !(*this == b);
  275. }
  276. /// Return true if this is less than b
  277. Bool operator<(const String& b) const
  278. {
  279. checkInit();
  280. b.checkInit();
  281. return std::strcmp(&m_data[0], &b.m_data[0]) < 0;
  282. }
  283. /// Return true if this is less or equal to b
  284. Bool operator<=(const String& b) const
  285. {
  286. checkInit();
  287. b.checkInit();
  288. return std::strcmp(&m_data[0], &b.m_data[0]) <= 0;
  289. }
  290. /// Return true if this is greater than b
  291. Bool operator>(const String& b) const
  292. {
  293. checkInit();
  294. b.checkInit();
  295. return std::strcmp(&m_data[0], &b.m_data[0]) > 0;
  296. }
  297. /// Return true if this is greater or equal to b
  298. Bool operator>=(const String& b) const
  299. {
  300. checkInit();
  301. b.checkInit();
  302. return std::strcmp(&m_data[0], &b.m_data[0]) >= 0;
  303. }
  304. /// Return true if strings are equal
  305. Bool operator==(const CStringType& cstr) const
  306. {
  307. checkInit();
  308. return std::strcmp(&m_data[0], cstr.get()) == 0;
  309. }
  310. /// Return true if strings are not equal
  311. Bool operator!=(const CStringType& cstr) const
  312. {
  313. return !(*this == cstr);
  314. }
  315. /// Return true if this is less than cstr.
  316. Bool operator<(const CStringType& cstr) const
  317. {
  318. checkInit();
  319. return std::strcmp(&m_data[0], cstr.get()) < 0;
  320. }
  321. /// Return true if this is less or equal to cstr.
  322. Bool operator<=(const CStringType& cstr) const
  323. {
  324. checkInit();
  325. return std::strcmp(&m_data[0], cstr.get()) <= 0;
  326. }
  327. /// Return true if this is greater than cstr.
  328. Bool operator>(const CStringType& cstr) const
  329. {
  330. checkInit();
  331. return std::strcmp(&m_data[0], cstr.get()) > 0;
  332. }
  333. /// Return true if this is greater or equal to cstr.
  334. Bool operator>=(const CStringType& cstr) const
  335. {
  336. checkInit();
  337. return std::strcmp(&m_data[0], cstr.get()) >= 0;
  338. }
  339. /// Return the string's length. It doesn't count the terminating character.
  340. PtrSize getLength() const
  341. {
  342. auto size = m_data.getSize();
  343. auto out = (size != 0) ? (size - 1) : 0;
  344. ANKI_ASSERT(std::strlen(&m_data[0]) == out);
  345. return out;
  346. }
  347. /// Return the CString.
  348. CStringType toCString() const
  349. {
  350. checkInit();
  351. return CStringType(&m_data[0]);
  352. }
  353. /// Append another string to this one.
  354. void append(Allocator alloc, const String& b)
  355. {
  356. if(!b.isEmpty())
  357. {
  358. appendInternal(alloc, &b.m_data[0], b.m_data.getSize());
  359. }
  360. }
  361. /// Append a const string to this one.
  362. void append(Allocator alloc, const CStringType& cstr)
  363. {
  364. if(!cstr.isEmpty())
  365. {
  366. appendInternal(alloc, &cstr[0], cstr.getLength() + 1);
  367. }
  368. }
  369. /// Create formated string.
  370. void sprintf(Allocator alloc, CString fmt, ...);
  371. /// Return true if it's empty.
  372. Bool isEmpty() const
  373. {
  374. return m_data.isEmpty();
  375. }
  376. /// Find a substring of this string.
  377. /// @param[in] cstr The substring to search.
  378. /// @param position Position of the first character in the string to be
  379. /// considered in the search.
  380. /// @return A valid position if the string is found or NPOS if not found.
  381. PtrSize find(const CStringType& cstr, PtrSize position = 0) const
  382. {
  383. checkInit();
  384. return toCString().find(cstr, position);
  385. }
  386. /// Find a substring of this string.
  387. /// @param[in] str The substring to search.
  388. /// @param position Position of the first character in the string to be
  389. /// considered in the search.
  390. /// @return A valid position if the string is found or NPOS if not found.
  391. PtrSize find(const String& str, PtrSize position) const
  392. {
  393. str.checkInit();
  394. return find(str.toCString(), position);
  395. }
  396. /// Convert a number to a string.
  397. template<typename TNumber>
  398. void toString(Allocator alloc, TNumber number);
  399. /// Convert to F64.
  400. ANKI_USE_RESULT Error toF64(F64& out) const
  401. {
  402. return toCString().toF64(out);
  403. }
  404. /// Convert to I64.
  405. ANKI_USE_RESULT Error toI64(I64& out) const
  406. {
  407. return toCString().toI64(out);
  408. }
  409. protected:
  410. DArray<Char> m_data;
  411. void checkInit() const
  412. {
  413. ANKI_ASSERT(m_data.getSize() > 1);
  414. }
  415. /// Append to this string.
  416. void appendInternal(Allocator alloc, const Char* str, PtrSize strSize);
  417. void move(String& b)
  418. {
  419. ANKI_ASSERT(this != &b);
  420. m_data = std::move(b.m_data);
  421. }
  422. };
  423. //==============================================================================
  424. template<typename TNumber>
  425. inline void String::toString(Allocator alloc, TNumber number)
  426. {
  427. destroy(alloc);
  428. Array<Char, 512> buff;
  429. I ret = std::snprintf(
  430. &buff[0], buff.size(), detail::toStringFormat<TNumber>(), number);
  431. if(ret < 0 || ret > static_cast<I>(buff.getSize()))
  432. {
  433. ANKI_LOGF("To small intermediate buffer");
  434. }
  435. else
  436. {
  437. create(alloc, &buff[0]);
  438. }
  439. }
  440. /// String with automatic cleanup.
  441. class StringAuto : public String
  442. {
  443. public:
  444. using Base = String;
  445. using Allocator = typename Base::Allocator;
  446. /// Create with allocator.
  447. StringAuto(Allocator alloc)
  448. : Base()
  449. , m_alloc(alloc)
  450. {
  451. }
  452. /// Move constructor.
  453. StringAuto(StringAuto&& b)
  454. : Base()
  455. {
  456. move(b);
  457. }
  458. /// Automatic destruction.
  459. ~StringAuto()
  460. {
  461. Base::destroy(m_alloc);
  462. }
  463. /// Initialize using a const string.
  464. void create(const CStringType& cstr)
  465. {
  466. Base::create(m_alloc, cstr);
  467. }
  468. /// Initialize using a range. Copies the range of [first, last)
  469. void create(ConstIterator first, ConstIterator last)
  470. {
  471. Base::create(m_alloc, first, last);
  472. }
  473. /// Initialize using a character.
  474. void create(Char c, PtrSize length)
  475. {
  476. Base::create(m_alloc, c, length);
  477. }
  478. /// Copy one string to this one.
  479. void create(const String& b)
  480. {
  481. Base::create(m_alloc, b.toCString());
  482. }
  483. /// Destroy the string.
  484. void destroy()
  485. {
  486. Base::destroy(m_alloc);
  487. }
  488. /// Move one string to this one.
  489. StringAuto& operator=(StringAuto&& b)
  490. {
  491. move(b);
  492. return *this;
  493. }
  494. /// Append another string to this one.
  495. void append(const String& b)
  496. {
  497. Base::append(m_alloc, b);
  498. }
  499. /// Append a const string to this one.
  500. void append(const CStringType& cstr)
  501. {
  502. Base::append(m_alloc, cstr);
  503. }
  504. /// Create formated string.
  505. template<typename... TArgs>
  506. void sprintf(CString fmt, TArgs... args)
  507. {
  508. Base::sprintf(m_alloc, fmt, args...);
  509. }
  510. /// Convert a number to a string.
  511. template<typename TNumber>
  512. void toString(TNumber number)
  513. {
  514. Base::toString(m_alloc, number);
  515. }
  516. private:
  517. GenericMemoryPoolAllocator<Char> m_alloc;
  518. void move(StringAuto& b)
  519. {
  520. Base::move(b);
  521. m_alloc = std::move(b.m_alloc);
  522. }
  523. };
  524. /// @}
  525. } // end namespace anki