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.

981 lines
32 KiB

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