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