A collection of basic/generally desirable code I use across multiple C++ projects.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

979 lines
32 KiB

1 year ago
  1. #ifndef STRING_H
  2. #define STRING_H
  3. #include "types.h"
  4. #include "alloc.h"
  5. #include <string.h> // @TODO remove this
  6. #define STB_SPRINTF_IMPLEMENTATION
  7. #define STB_SPRINTF_STATIC
  8. #include <stb/stb_sprintf.h>
  9. #define STR_MEMALLOC pMalloc
  10. #define STR_MEMFREE pFree
  11. #define STR_ASSERT assert
  12. #define STR_IMPLEMENTATION
  13. #define STR_SUPPORT_STD_STRING 0
  14. #define STR_DEFINE_STR32 0 // the type Str32, which would normally be available, conflicts with a type in MacTypes.h
  15. // 'String' is a datatype, but it also is a namespace for a bunch of static 'char*' operations that
  16. // you would normally find in the <cstring> or <string.h> header
  17. class String {
  18. public:
  19. // Static empty buffer we can point to for empty strings
  20. // Pointing to a literal increases the like-hood of getting a crash if someone attempts to write in the empty string buffer.
  21. constexpr static char* EmptyBuffer = (char*) "\0NULL";
  22. constexpr static unsigned char ASCII_LOWER[128] = {
  23. 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
  24. 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
  25. 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
  26. 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
  27. 64, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111,
  28. 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 91, 92, 93, 94, 95,
  29. 96, 97, 98, 99, 100,101,102,103,104,105,106,107,108,109,110,111,
  30. 112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127
  31. };
  32. constexpr static unsigned char ASCII_UPPER[128] = {
  33. 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
  34. 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
  35. 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
  36. 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
  37. 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
  38. 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
  39. 96, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
  40. 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90,123,124,125,126,127
  41. };
  42. static inline s32 sprintf(char* buffer, const char* format, ...) {
  43. TYPES_H_FTAG;
  44. va_list args;
  45. va_start(args, format);
  46. s32 code = stbsp_vsprintf(buffer, format, args);
  47. va_end(args);
  48. return code;
  49. }
  50. static inline s32 snprintf(char* buffer, s32 count, const char* format, ...) {
  51. TYPES_H_FTAG;
  52. va_list args;
  53. va_start(args, format);
  54. s32 code = stbsp_vsnprintf(buffer, count, format, args);
  55. va_end(args);
  56. return code;
  57. }
  58. static inline s32 vsnprintf(char* buffer, int count, const char* format, va_list args) {
  59. return stbsp_vsnprintf(buffer, count, format, args);
  60. }
  61. static inline bool isDigit(char c) {
  62. TYPES_H_FTAG;
  63. return (c >= '0') && (c <= '9');
  64. }
  65. static inline bool isAlpha(char c) {
  66. TYPES_H_FTAG;
  67. return (c >= 'A' && c <= 'Z')
  68. || (c >= 'a' && c <= 'z');
  69. }
  70. static inline bool isHexDigit(char c) {
  71. TYPES_H_FTAG;
  72. return ((c >= '0') && (c <= '9'))
  73. || ((c >= 'A') && (c <= 'F'))
  74. || ((c >= 'a') && (c <= 'f'));
  75. }
  76. static inline bool isOctalDigit(char c) {
  77. TYPES_H_FTAG;
  78. return (c >= '0') && (c <= '7');
  79. }
  80. static inline bool isBinaryDigit(char c) {
  81. TYPES_H_FTAG;
  82. return c == '0' || c == '1';
  83. }
  84. static inline char* intToString(u64 integer) {
  85. TYPES_H_FTAG;
  86. u32 capacity = 10;
  87. u32* remainders = (u32*) pMalloc(sizeof (u32) * capacity);
  88. u32 count = 0;
  89. while (true) {
  90. if (capacity <= count) {
  91. capacity *= 2;
  92. remainders = (u32*) pRealloc(remainders, sizeof (u32) * capacity);
  93. }
  94. remainders[count++] = integer % 10;
  95. integer /= 10;
  96. if (integer == 0) break;
  97. }
  98. char* buffer = (char*) pMalloc(sizeof (char) * count + 1);
  99. for (u32 i = 0; i < count; i++) {
  100. buffer[count - i - 1] = '0' + remainders[i];
  101. }
  102. buffer[count] = '\0';
  103. pFree(remainders);
  104. return buffer;
  105. }
  106. static inline u64 hexStringToInt(const char* str) {
  107. TYPES_H_FTAG;
  108. u64 out = 0;
  109. while (*str != '\0') {
  110. u8 byte = *str++;
  111. if ((byte >= '0') && (byte <= '9')) {
  112. byte = byte - '0';
  113. } else if ((byte >= 'a') && (byte <= 'f')) {
  114. byte = byte - 'a' + 10;
  115. } else if ((byte >= 'A') && (byte <= 'F')) {
  116. byte = byte - 'A' + 10;
  117. }
  118. // only use the last four bits - precision of a single hex digit
  119. out = (out << 4) | (byte & 0xF);
  120. }
  121. return out;
  122. }
  123. static inline u32 len(const char* string) {
  124. TYPES_H_FTAG;
  125. const char* start = string;
  126. while (*string++ != '\0') {}
  127. return (u32) (string - start);
  128. }
  129. // returns true if null-terminated strings |s1| and |s2| are equal
  130. static inline bool eq(const char* s1, const char* s2) {
  131. TYPES_H_FTAG;
  132. u32 l1 = String::len(s1);
  133. u32 l2 = String::len(s2);
  134. if (l1 != l2) return false;
  135. for (u32 i = 0; i < l1; i++) {
  136. if (s1[i] != s2[i]) {
  137. return false;
  138. }
  139. }
  140. return true;
  141. }
  142. // same as |eq|, but handles |s1| and/or |s2| being null
  143. static inline bool eqNullCheck(const char* s1, const char* s2) {
  144. TYPES_H_FTAG;
  145. if (s1 == null) {
  146. if (s2 == null) {
  147. return true;
  148. } else {
  149. return false;
  150. }
  151. } else if (s2 == null) {
  152. return false;
  153. }
  154. return String::eq(s1, s2);
  155. }
  156. // heap allocates a copy of |string| and returns a pointer to it.
  157. static inline char* cpy(const char* string, u32 length, Allocator* allocator = Allocator::GetDefault()) {
  158. TYPES_H_FTAG;
  159. char* buffer = (char*) allocator->mallocate(sizeof (char) * (length + 1), allocator->state);
  160. u32 i = 0;
  161. for (; i < length; i++) {
  162. buffer[i] = string[i];
  163. }
  164. buffer[i] = '\0';
  165. return buffer;
  166. }
  167. // heap allocates a copy of |string| and returns a pointer to it.
  168. static inline char* cpy(const char* string, Allocator* allocator = Allocator::GetDefault()) {
  169. TYPES_H_FTAG;
  170. u32 len = String::len(string);
  171. return String::cpy(string, len, allocator = Allocator::GetDefault());
  172. }
  173. static inline bool memeq(const unsigned char* m1, const unsigned char* m2, size_t length) {
  174. TYPES_H_FTAG;
  175. return memcmp(m1, m2, length) == 0;
  176. }
  177. static inline bool memeq(const unsigned char* m1, size_t l1, const unsigned char* m2, size_t l2) {
  178. TYPES_H_FTAG;
  179. if (l1 != l2) return false;
  180. return memeq(m1, m2, l1);
  181. }
  182. #ifdef _WIN32
  183. static inline size_t wcharToChar(wchar_t* wstring, char* buffer, size_t maxBufferLength) {
  184. TYPES_H_FTAG;
  185. return wcstombs(buffer, wstring, maxBufferLength);
  186. }
  187. #endif
  188. static inline void* memset(void* p, char c, u32 length) {
  189. TYPES_H_FTAG;
  190. //__stosb((unsigned char*) p, c, length);
  191. char* a = (char*) p;
  192. for (u32 i = 0; i < length; i++) a[i] = c;
  193. return a;
  194. }
  195. static inline void memcpy(void* dest, void* src, u32 size) {
  196. TYPES_H_FTAG;
  197. u8* dest_ = (u8*) dest;
  198. u8* src_ = (u8*) src;
  199. for (u32 i = 0; i < size; i++) {
  200. dest_[i] = src_[i];
  201. }
  202. }
  203. // replace all instances of |c1| in |string| with |c2|
  204. static inline void replaceC(char* string, u32 length, char c1, char c2) {
  205. TYPES_H_FTAG;
  206. for (u32 i = 0; i < length; i++) {
  207. if (string[i] == c1) {
  208. string[i] = c2;
  209. }
  210. }
  211. }
  212. static inline const char* firstCharOccurence(const char* string, u32 length, char c) {
  213. TYPES_H_FTAG;
  214. for (s32 i = 0; i < length; i++) {
  215. const char* s = string + i;
  216. if (*s == c) {
  217. return s;
  218. }
  219. }
  220. return null;
  221. }
  222. static inline const char* firstCharOccurence(const char* string, char c) {
  223. TYPES_H_FTAG;
  224. return String::firstCharOccurence(string, String::len(string), c);
  225. }
  226. static inline const char* lastCharOccurence(const char* string, u32 length, char c) {
  227. TYPES_H_FTAG;
  228. for (s32 i = length - 1; i >= 0; i--) { // @NOTE 'i' needs to be a signed int here...
  229. if (*(string + i) == c) {
  230. return string + i;
  231. }
  232. }
  233. return null;
  234. }
  235. static inline const char* lastCharOccurence(const char* string, char c) {
  236. TYPES_H_FTAG;
  237. return String::lastCharOccurence(string, String::len(string), c);
  238. }
  239. static inline bool hasSuffix(const char* string, const char* suffix) {
  240. TYPES_H_FTAG;
  241. const char* p = String::lastCharOccurence(string, String::len(string), suffix[0]);
  242. if (p) return String::eq(p, suffix);
  243. return false;
  244. }
  245. static inline u32 countLines(const char* buffer) {
  246. TYPES_H_FTAG;
  247. u32 lines = 0;
  248. char c;
  249. while ((c = *buffer) != '\0') {
  250. if (c == '\n') lines++;
  251. buffer++;
  252. }
  253. return lines;
  254. }
  255. static inline bool isAscii(const char* buffer, u32 length) {
  256. TYPES_H_FTAG;
  257. const unsigned char* ubuffer = (const unsigned char*) buffer;
  258. for (u32 i = 0; i < length; i++) {
  259. if (ubuffer[i] & 128) { // binary: 0b 1000 0000
  260. return false;
  261. }
  262. }
  263. return true;
  264. }
  265. static inline bool isAsciiWhitespace(char c) {
  266. TYPES_H_FTAG;
  267. switch (c) {
  268. //case '\b':
  269. //case '\v':
  270. //case '\f':
  271. case '\r':
  272. case '\t':
  273. case '\n':
  274. case ' ':
  275. return true;
  276. default:
  277. return false;
  278. }
  279. }
  280. // static inline bool isUnicodeSpaceSeparator(wide character);
  281. // @TODO ALL OF THESE TRIMS
  282. //static inline char* trimStart(const char* str, u32 count);
  283. //static inline char* trimEnd(const char* str, u32 count);
  284. static inline char* trim(const char* str, u32 count, Allocator* allocator = Allocator::GetDefault()) {
  285. TYPES_H_FTAG;
  286. u32 length = String::len(str);
  287. if (length <= count) {
  288. return (char*) "";
  289. }
  290. char* buffer = (char*) allocator->mallocate(sizeof (char) * (length - 1), allocator->state);
  291. u32 i = 0;
  292. for (; i < (length - count); i++) {
  293. buffer[i] = str[i + 1];
  294. }
  295. buffer[i] = '\0';
  296. return buffer;
  297. }
  298. static inline char* asciiToLower(const char* str, Allocator* allocator = Allocator::GetDefault()) {
  299. TYPES_H_FTAG;
  300. u32 length = String::len(str);
  301. char* buffer = (char*) allocator->mallocate(sizeof (char) * length + 1, allocator->state);
  302. u32 i = 0;
  303. for (; i < length; i++) {
  304. buffer[i] = String::ASCII_LOWER[str[i]];
  305. }
  306. buffer[i] = '\0';
  307. return buffer;
  308. }
  309. static inline char* asciiToUpper(const char* str, Allocator* allocator = Allocator::GetDefault()) {
  310. TYPES_H_FTAG;
  311. u32 length = String::len(str);
  312. char* buffer = (char*) allocator->mallocate(sizeof (char) * length + 1, allocator->state);
  313. u32 i = 0;
  314. for (; i < length; i++) {
  315. buffer[i] = String::ASCII_LOWER[str[i]];
  316. }
  317. buffer[i] = '\0';
  318. return buffer;
  319. }
  320. static inline char* concat(const char* str1, const char* str2, Allocator* allocator = Allocator::GetDefault()) {
  321. TYPES_H_FTAG;
  322. u32 l1 = String::len(str1);
  323. u32 l2 = String::len(str2);
  324. u32 newLength = l1 + l2;
  325. char* newBuffer = (char*) allocator->mallocate(sizeof (char) * newLength + 1, allocator->state);
  326. u32 i = 0;
  327. for (; i < newLength; i++) {
  328. if (i < l1) {
  329. newBuffer[i] = str1[i];
  330. } else {
  331. newBuffer[i] = str2[i - l1];
  332. }
  333. }
  334. newBuffer[i] = '\0';
  335. return newBuffer;
  336. }
  337. static inline u32 write(char* dest, const char* src, u32 length) {
  338. TYPES_H_FTAG;
  339. u32 i = 0;
  340. for (; i < length; i++) {
  341. dest[i] = src[i];
  342. }
  343. dest[i] = '\0';
  344. return i;
  345. }
  346. // returns the number of characters written.
  347. static inline u32 write(char* dest, const char* src) {
  348. TYPES_H_FTAG;
  349. u32 length = String::len(src);
  350. return String::write(dest, src, length);
  351. }
  352. static inline char* read(const char* buffer, u32 length, Allocator* allocator = Allocator::GetDefault()) {
  353. TYPES_H_FTAG;
  354. char* tk = (char*) allocator->mallocate(sizeof (char) * length + 1, allocator->state);
  355. u32 i = 0;
  356. while (i < length) {
  357. tk[i] = *(buffer + i);
  358. i++;
  359. }
  360. tk[i] = '\0';
  361. return tk;
  362. }
  363. char* Data; // Point to LocalBuf() or heap allocated
  364. int Capacity : 21; // Max 2 MB
  365. int LocalBufSize : 10; // Max 1023 bytes
  366. unsigned int Owned : 1; // Set when we have ownership of the pointed data (most common, unless using set_ref() method or StringRef constructor)
  367. inline char* c_str() { TYPES_H_FTAG; return Data; }
  368. inline const char* c_str() const { TYPES_H_FTAG; return Data; }
  369. inline bool empty() const { TYPES_H_FTAG; return Data[0] == 0; }
  370. inline int length() const { TYPES_H_FTAG; return (int)strlen(Data); } // by design, allow user to write into the buffer at any time
  371. inline int capacity() const { TYPES_H_FTAG; return Capacity; }
  372. inline bool owned() const { TYPES_H_FTAG; return Owned ? true : false; }
  373. inline char& operator[](size_t i) { TYPES_H_FTAG; return Data[i]; }
  374. inline char operator[](size_t i) const { TYPES_H_FTAG; return Data[i]; }
  375. inline String& operator=(const String& rhs) { TYPES_H_FTAG; set(rhs); return *this; }
  376. inline bool operator==(const String& rhs) const { TYPES_H_FTAG; return strcmp(c_str(), rhs.c_str()) == 0; }
  377. inline String& operator=(const char* rhs) { TYPES_H_FTAG; set(rhs); return *this; }
  378. inline bool operator==(const char* rhs) const { TYPES_H_FTAG; return strcmp(c_str(), rhs) == 0; }
  379. inline String() {
  380. TYPES_H_FTAG;
  381. Data = EmptyBuffer; // Shared READ-ONLY initial buffer for 0 capacity
  382. Capacity = 0;
  383. LocalBufSize = 0;
  384. Owned = 0;
  385. }
  386. inline String(const String& rhs) : String() {
  387. TYPES_H_FTAG;
  388. set(rhs);
  389. }
  390. inline String(const char* rhs) : String() {
  391. TYPES_H_FTAG;
  392. set(rhs);
  393. }
  394. inline void set_ref(const char* src) {
  395. TYPES_H_FTAG;
  396. if (Owned && !is_using_local_buf())
  397. STR_MEMFREE(Data);
  398. Data = src ? (char*)src : EmptyBuffer;
  399. Capacity = 0;
  400. Owned = 0;
  401. }
  402. inline void set(const String& src) {
  403. TYPES_H_FTAG;
  404. int buf_len = (int)strlen(src.c_str())+1;
  405. if ((int)Capacity < buf_len)
  406. reserve_discard(buf_len);
  407. memcpy(Data, (void*)src.c_str(), (size_t)buf_len);
  408. Owned = 1;
  409. }
  410. inline void set(const char* src) {
  411. TYPES_H_FTAG;
  412. // We allow set(NULL) or via = operator to clear the string.
  413. if (src == NULL)
  414. {
  415. clear();
  416. return;
  417. }
  418. int buf_len = (int)strlen(src)+1;
  419. if (Capacity < buf_len)
  420. reserve_discard(buf_len);
  421. memcpy(Data, (void*)src, (size_t)buf_len);
  422. Owned = 1;
  423. }
  424. inline void set(const char* src, const char* src_end) {
  425. TYPES_H_FTAG;
  426. STR_ASSERT(src != NULL && src_end >= src);
  427. int buf_len = (int)(src_end-src)+1;
  428. if ((int)Capacity < buf_len)
  429. reserve_discard(buf_len);
  430. memcpy(Data, (void*)src, (size_t)(buf_len - 1));
  431. Data[buf_len-1] = 0;
  432. Owned = 1;
  433. }
  434. // Clear
  435. inline void clear() {
  436. TYPES_H_FTAG;
  437. if (Owned && !is_using_local_buf())
  438. STR_MEMFREE(Data);
  439. if (LocalBufSize) {
  440. Data = local_buf();
  441. Data[0] = '\0';
  442. Capacity = LocalBufSize;
  443. Owned = 1;
  444. } else {
  445. Data = EmptyBuffer;
  446. Capacity = 0;
  447. Owned = 0;
  448. }
  449. }
  450. // Reserve memory, preserving the current of the buffer
  451. inline void reserve(int new_capacity) {
  452. TYPES_H_FTAG;
  453. if (new_capacity <= Capacity)
  454. return;
  455. char* new_data;
  456. if (new_capacity < LocalBufSize) {
  457. // Disowned -> LocalBuf
  458. new_data = local_buf();
  459. new_capacity = LocalBufSize;
  460. } else {
  461. // Disowned or LocalBuf -> Heap
  462. new_data = (char*)STR_MEMALLOC((size_t)new_capacity * sizeof(char));
  463. }
  464. // string in Data might be longer than new_capacity if it wasn't owned, don't copy too much
  465. #ifdef _MSC_VER
  466. strncpy_s(new_data, (size_t)new_capacity, Data, (size_t)new_capacity - 1);
  467. #else
  468. strncpy(new_data, Data, (size_t)new_capacity - 1);
  469. #endif
  470. new_data[new_capacity - 1] = 0;
  471. if (Owned && !is_using_local_buf())
  472. STR_MEMFREE(Data);
  473. Data = new_data;
  474. Capacity = new_capacity;
  475. Owned = 1;
  476. }
  477. // Reserve memory, discarding the current of the buffer (if we expect to be fully rewritten)
  478. inline void reserve_discard(int new_capacity) {
  479. TYPES_H_FTAG;
  480. if (new_capacity <= Capacity)
  481. return;
  482. if (Owned && !is_using_local_buf())
  483. STR_MEMFREE(Data);
  484. if (new_capacity < LocalBufSize) {
  485. // Disowned -> LocalBuf
  486. Data = local_buf();
  487. Capacity = LocalBufSize;
  488. } else {
  489. // Disowned or LocalBuf -> Heap
  490. Data = (char*)STR_MEMALLOC((size_t)new_capacity * sizeof(char));
  491. Capacity = new_capacity;
  492. }
  493. Owned = 1;
  494. }
  495. inline void shrink_to_fit() {
  496. TYPES_H_FTAG;
  497. if (!Owned || is_using_local_buf()) return;
  498. int new_capacity = length() + 1;
  499. if (Capacity <= new_capacity) return;
  500. char* new_data = (char*)STR_MEMALLOC((size_t)new_capacity * sizeof(char));
  501. memcpy(new_data, Data, (size_t)new_capacity);
  502. STR_MEMFREE(Data);
  503. Data = new_data;
  504. Capacity = new_capacity;
  505. }
  506. // FIXME: merge setfv() and appendfv()?
  507. inline int setfv(const char* fmt, va_list args) {
  508. TYPES_H_FTAG;
  509. // Needed for portability on platforms where va_list are passed by reference and modified by functions
  510. va_list args2;
  511. va_copy(args2, args);
  512. // First try
  513. int len = vsnprintf(Owned ? Data : NULL, Owned ? (size_t)Capacity : 0, fmt, args);
  514. STR_ASSERT(len >= 0);
  515. if (Capacity < len + 1)
  516. {
  517. reserve_discard(len + 1);
  518. len = vsnprintf(Data, (size_t)len + 1, fmt, args2);
  519. }
  520. STR_ASSERT(Owned);
  521. return len;
  522. }
  523. inline int setf(const char* fmt, ...) {
  524. TYPES_H_FTAG;
  525. va_list args;
  526. va_start(args, fmt);
  527. int len = setfv(fmt, args);
  528. va_end(args);
  529. return len;
  530. }
  531. inline int setfv_nogrow(const char* fmt, va_list args) {
  532. TYPES_H_FTAG;
  533. STR_ASSERT(Owned);
  534. if (Capacity == 0) return 0;
  535. int w = vsnprintf(Data, (size_t)Capacity, fmt, args);
  536. Data[Capacity - 1] = 0;
  537. Owned = 1;
  538. return (w == -1) ? Capacity - 1 : w;
  539. }
  540. inline int setf_nogrow(const char* fmt, ...) {
  541. TYPES_H_FTAG;
  542. va_list args;
  543. va_start(args, fmt);
  544. int len = setfv_nogrow(fmt, args);
  545. va_end(args);
  546. return len;
  547. }
  548. inline int append_from(int idx, char c) {
  549. TYPES_H_FTAG;
  550. int add_len = 1;
  551. if (Capacity < idx + add_len + 1)
  552. reserve(idx + add_len + 1);
  553. Data[idx] = c;
  554. Data[idx + add_len] = 0;
  555. STR_ASSERT(Owned);
  556. return add_len;
  557. }
  558. inline int append_from(int idx, const char* s, const char* s_end) {
  559. TYPES_H_FTAG;
  560. if (!s_end) s_end = s + strlen(s);
  561. int add_len = (int)(s_end - s);
  562. if (Capacity < idx + add_len + 1) reserve(idx + add_len + 1);
  563. memcpy(Data + idx, (void*)s, (size_t)add_len);
  564. Data[idx + add_len] = 0; // Our source data isn't necessarily zero-terminated
  565. STR_ASSERT(Owned);
  566. return add_len;
  567. }
  568. // FIXME: merge setfv() and appendfv()?
  569. inline int appendfv_from(int idx, const char* fmt, va_list args) {
  570. TYPES_H_FTAG;
  571. // Needed for portability on platforms where va_list are passed by reference and modified by functions
  572. va_list args2;
  573. va_copy(args2, args);
  574. // First try
  575. int add_len = vsnprintf(Owned ? Data + idx : NULL, Owned ? (size_t)(Capacity - idx) : 0, fmt, args);
  576. STR_ASSERT(add_len >= 0);
  577. if (Capacity < idx + add_len + 1) {
  578. reserve(idx + add_len + 1);
  579. add_len = vsnprintf(Data + idx, (size_t)add_len + 1, fmt, args2);
  580. }
  581. STR_ASSERT(Owned);
  582. return add_len;
  583. }
  584. inline int appendf_from(int idx, const char* fmt, ...) {
  585. TYPES_H_FTAG;
  586. va_list args;
  587. va_start(args, fmt);
  588. int len = appendfv_from(idx, fmt, args);
  589. va_end(args);
  590. return len;
  591. }
  592. inline int append(char c) {
  593. TYPES_H_FTAG;
  594. int cur_len = length();
  595. return append_from(cur_len, c);
  596. }
  597. inline int append(const char* s, const char* s_end = null) {
  598. TYPES_H_FTAG;
  599. int cur_len = length();
  600. return append_from(cur_len, s, s_end);
  601. }
  602. inline int appendfv(const char* fmt, va_list args) {
  603. TYPES_H_FTAG;
  604. int cur_len = length();
  605. return appendfv_from(cur_len, fmt, args);
  606. }
  607. int appendf(const char* fmt, ...) {
  608. TYPES_H_FTAG;
  609. va_list args;
  610. va_start(args, fmt);
  611. int len = appendfv(fmt, args);
  612. va_end(args);
  613. return len;
  614. }
  615. // Destructor for all variants
  616. inline ~String()
  617. {
  618. if (Owned && !is_using_local_buf())
  619. STR_MEMFREE(Data);
  620. }
  621. protected:
  622. inline char* local_buf() { TYPES_H_FTAG; return (char*)this + sizeof(String); }
  623. inline const char* local_buf() const { TYPES_H_FTAG; return (char*)this + sizeof(String); }
  624. inline bool is_using_local_buf() const { TYPES_H_FTAG; return Data == local_buf() && LocalBufSize != 0; }
  625. // Constructor for StringXXX variants with local buffer
  626. String(unsigned short local_buf_size) {
  627. TYPES_H_FTAG;
  628. STR_ASSERT(local_buf_size < 1024);
  629. Data = local_buf();
  630. Data[0] = '\0';
  631. Capacity = local_buf_size;
  632. LocalBufSize = local_buf_size;
  633. Owned = 1;
  634. }
  635. };
  636. // Literal/reference string
  637. class StringRef : public String {
  638. public:
  639. StringRef(const char* s) : String() { TYPES_H_FTAG; set_ref(s); }
  640. };
  641. // Types embedding a local buffer
  642. // NB: we need to override the constructor and = operator for both String& and TYPENAME (without the later compiler will call a default copy operator)
  643. #define STR_DEFINETYPE(TYPENAME, LOCALBUFSIZE) \
  644. class TYPENAME : public String \
  645. { \
  646. char local_buf[LOCALBUFSIZE]; \
  647. public: \
  648. TYPENAME() : String(LOCALBUFSIZE) {} \
  649. TYPENAME(const String& rhs) : String(LOCALBUFSIZE) { TYPES_H_FTAG; set(rhs); } \
  650. TYPENAME(const char* rhs) : String(LOCALBUFSIZE) { TYPES_H_FTAG; set(rhs); } \
  651. TYPENAME(const TYPENAME& rhs) : String(LOCALBUFSIZE) { TYPES_H_FTAG; set(rhs); } \
  652. TYPENAME& operator=(const char* rhs) { TYPES_H_FTAG; set(rhs); return *this; } \
  653. TYPENAME& operator=(const String& rhs) { TYPES_H_FTAG; set(rhs); return *this; } \
  654. TYPENAME& operator=(const TYPENAME& rhs) { TYPES_H_FTAG; set(rhs); return *this; } \
  655. };
  656. // Disable PVS-Studio warning V730: Not all members of a class are initialized inside the constructor (local_buf is not initialized and that is fine)
  657. // -V:STR_DEFINETYPE:730
  658. // Helper to define StringXXXf constructors
  659. #define STR_DEFINETYPE_F(TYPENAME, TYPENAME_F) \
  660. class TYPENAME_F : public TYPENAME \
  661. { \
  662. public: \
  663. TYPENAME_F(const char* fmt, ...) : TYPENAME() { TYPES_H_FTAG; va_list args; va_start(args, fmt); setfv(fmt, args); va_end(args); } \
  664. };
  665. #ifdef __clang__
  666. #pragma clang diagnostic push
  667. #pragma clang diagnostic ignored "-Wunused-private-field" // warning : private field 'local_buf' is not used
  668. #endif
  669. // Declaring types for common sizes here
  670. STR_DEFINETYPE(String16, 16)
  671. STR_DEFINETYPE(String30, 30)
  672. STR_DEFINETYPE(String64, 64)
  673. STR_DEFINETYPE(String128, 128)
  674. STR_DEFINETYPE(String256, 256)
  675. STR_DEFINETYPE(String512, 512)
  676. // Declaring helper constructors to pass in format strings in one statement
  677. STR_DEFINETYPE_F(String16, String16f)
  678. STR_DEFINETYPE_F(String30, String30f)
  679. STR_DEFINETYPE_F(String64, String64f)
  680. STR_DEFINETYPE_F(String128, String128f)
  681. STR_DEFINETYPE_F(String256, String256f)
  682. STR_DEFINETYPE_F(String512, String512f)
  683. #if STR_DEFINE_STR32
  684. STR_DEFINETYPE(String32, 32)
  685. STR_DEFINETYPE_F(String32, String32f)
  686. #endif
  687. // by default, if you use a string type with a local buffer, and append to it with something bigger than the local buffer,
  688. // you end up realloc'ing space to fit, and this reallocation is stingy - only exactly as much as needed.
  689. // if you are creating a string which may be repeatedly concatenated with other strings/appended to, it's better
  690. // to do a smaller number of growing allocations along the lines of what dynamic arrays do.
  691. // this type is the same as other string types, but if it has to reserve additional space for itself, it will do so
  692. // eagerly, at a rate of 1.5x
  693. #define STRINGBUFFER_DEFINETYPE(TYPENAME, LOCALBUFSIZE) \
  694. class TYPENAME : public String { \
  695. char local_buf[LOCALBUFSIZE]; \
  696. public: \
  697. TYPENAME(const char* fmt, ...) : String(LOCALBUFSIZE) { TYPES_H_FTAG; va_list args; va_start(args, fmt); setfv(fmt, args); va_end(args); } \
  698. TYPENAME() : String(LOCALBUFSIZE) { TYPES_H_FTAG; } \
  699. TYPENAME(const String& rhs) : String(LOCALBUFSIZE) { TYPES_H_FTAG; set(rhs); } \
  700. TYPENAME(const char* rhs) : String(LOCALBUFSIZE) { TYPES_H_FTAG; set(rhs); } \
  701. TYPENAME(const TYPENAME& rhs) : String(LOCALBUFSIZE) { TYPES_H_FTAG; set(rhs); } \
  702. TYPENAME& operator=(const char* rhs) { TYPES_H_FTAG; set(rhs); return *this; } \
  703. TYPENAME& operator=(const String& rhs) { TYPES_H_FTAG; set(rhs); return *this; } \
  704. TYPENAME& operator=(const TYPENAME& rhs) { TYPES_H_FTAG; set(rhs); return *this; } \
  705. void reserve(int new_capacity) { \
  706. TYPES_H_FTAG; \
  707. if (new_capacity <= Capacity) \
  708. return; \
  709. char* new_data; \
  710. if (new_capacity < LocalBufSize) { \
  711. new_data = (char*)this + sizeof(String); \
  712. new_capacity = LocalBufSize; \
  713. } else { \
  714. new_data = (char*)STR_MEMALLOC((size_t)new_capacity * sizeof(char)); \
  715. } \
  716. strncpy(new_data, Data, (size_t)new_capacity - 1); \
  717. new_data[new_capacity - 1] = 0; \
  718. if (Owned && !is_using_local_buf()) \
  719. STR_MEMFREE(Data); \
  720. Data = new_data; \
  721. Capacity = new_capacity; \
  722. Owned = 1; \
  723. } \
  724. void reserve_discard(int new_capacity) { \
  725. TYPES_H_FTAG; \
  726. if (new_capacity <= Capacity) \
  727. return; \
  728. if (Owned && !is_using_local_buf()) \
  729. STR_MEMFREE(Data); \
  730. if (new_capacity < LocalBufSize) { \
  731. Data = (char*)this + sizeof(String); \
  732. Capacity = LocalBufSize; \
  733. } else { \
  734. while (Capacity < new_capacity) { \
  735. Capacity *= 1.5; \
  736. Data = (char*) STR_MEMALLOC((size_t) Capacity * sizeof(char)); \
  737. } \
  738. } \
  739. Owned = 1; \
  740. } \
  741. };
  742. STRINGBUFFER_DEFINETYPE(StringBuffer512, 512)
  743. #ifdef __clang__
  744. #pragma clang diagnostic pop
  745. #endif
  746. // On some platform vsnprintf() takes va_list by reference and modifies it.
  747. // va_copy is the 'correct' way to copy a va_list but Visual Studio prior to 2013 doesn't have it.
  748. #ifndef va_copy
  749. #define va_copy(dest, src) (dest = src)
  750. #endif
  751. //-------------------------------------------------------------------------
  752. // https://www.fileformat.info/info/unicode/category/Zs/list.htm
  753. /* @TODO
  754. bool isUnicodeSpaceSeparator(char c) {
  755. switch (c) {
  756. case 0x20:
  757. case 0xA0:
  758. case 0x1680:
  759. case 0x2000:
  760. case 0x2001:
  761. case 0x2002:
  762. case 0x2003:
  763. case 0x2004:
  764. case 0x2005:
  765. case 0x2006:
  766. case 0x2007:
  767. case 0x2008:
  768. case 0x2009:
  769. case 0x200A:
  770. case 0x202F:
  771. case 0x205F:
  772. case 0x3000:
  773. return true;
  774. default:
  775. return false;
  776. }
  777. }
  778. */
  779. //struct StringBuffer {
  780. // u32 length;
  781. // u32 capacity;
  782. // char* data;
  783. //
  784. // StringBuffer(u32 initialSize = 2048);
  785. // StringBuffer(const char* string);
  786. // ~StringBuffer();
  787. //
  788. // void checkIfShouldGrow();
  789. // bool isEmpty() const;
  790. // bool isFull() const;
  791. // char pop();
  792. // u32 append(char e);
  793. //};
  794. ////================================================================================
  795. //StringBuffer::StringBuffer(u32 initialSize) {
  796. // TYPES_H_FTAG;
  797. // this->length = 0;
  798. // this->capacity = initialSize;
  799. // this->data = (char*) pMalloc(sizeof(char) * this->capacity);
  800. //}
  801. //
  802. //StringBuffer::StringBuffer(const char* string) {
  803. // this->length = String::len(string);
  804. // this->capacity = this->length;
  805. // this->data = cpy(string, this->length);
  806. //}
  807. //
  808. //StringBuffer::~StringBuffer() {
  809. // pFree(this->data);
  810. //}
  811. //
  812. //void StringBuffer::checkIfShouldGrow() {
  813. // TYPES_H_FTAG;
  814. // if (this->isFull()) {
  815. // // optimal number as you approach infinite elements approaches PHI, but 1.5 sometimes works better for finite sizes
  816. // // more testing is probably needed
  817. // this->capacity = (u32) (this->capacity * 1.5);
  818. // this->data = (char*) pRealloc(this->data, sizeof(char) * this->capacity);
  819. // }
  820. //}
  821. //
  822. //bool StringBuffer::isEmpty() const {
  823. // TYPES_H_FTAG;
  824. // return this->length == 0;
  825. //}
  826. //
  827. //bool StringBuffer::isFull() const {
  828. // TYPES_H_FTAG;
  829. // return this->length == this->capacity;
  830. //}
  831. //
  832. //char StringBuffer::pop() {
  833. // TYPES_H_FTAG;
  834. // if (this->isEmpty()) {
  835. // die("empty");
  836. // }
  837. //
  838. // return this->data[--this->length];
  839. //}
  840. //
  841. //u32 StringBuffer::append(char e) {
  842. // TYPES_H_FTAG;
  843. // this->checkIfShouldGrow();
  844. //
  845. // this->data[this->length++] = e;
  846. //
  847. // return this->length - 1;
  848. //}
  849. #endif