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.

1016 lines
34 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 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 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 <cstring> // memcpy, memset, memcmp
  8. #define STB_SPRINTF_IMPLEMENTATION
  9. #define STB_SPRINTF_STATIC
  10. #include <stb/stb_sprintf.h>
  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. static Allocator* _stringClassAllocator = &Allocator::GetDefault(); // only used for the string class, string functions you pass an allocator if it allocates
  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) { // @ALLOC
  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. assert(&allocator == &Allocator::GetDefault());
  162. char* buffer = (char*) pMalloc(sizeof(char) * (length+1)); //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. // assumes null termination.
  186. static inline bool LexographicComparisonASCII(const char* str1, const char* str2) {
  187. u32 min;
  188. const u32 l1 = String::len(str1);
  189. const u32 l2 = String::len(str2);
  190. if (l1 > l2) {
  191. min = l2;
  192. } else {
  193. min = l1;
  194. }
  195. for (u32 i = 0; i < min; i++) {
  196. if (str1[i] < str2[i]) {
  197. return true;
  198. } else if (str1[i] > str2[i]) {
  199. return false;
  200. }
  201. }
  202. return l1 < l2;
  203. }
  204. #ifdef _WIN32
  205. static inline size_t wcharToChar(wchar_t* wstring, char* buffer, size_t maxBufferLength) {
  206. ULE_TYPES_H_FTAG;
  207. return wcstombs(buffer, wstring, maxBufferLength);
  208. }
  209. #endif
  210. static inline void* memset(void* p, char c, u32 length) {
  211. ULE_TYPES_H_FTAG;
  212. //__stosb((unsigned char*) p, c, length);
  213. char* a = (char*) p;
  214. for (u32 i = 0; i < length; i++) a[i] = c;
  215. return a;
  216. }
  217. static inline void memcpy(void* dest, void* src, u32 size) {
  218. ULE_TYPES_H_FTAG;
  219. // allowing c++ compilers to know we're invoking memcpy allows more aggressive optimizations, sometimes resulting in 8-9x speedup,
  220. // when compared to the horrible, commented out loop below.
  221. std::memcpy(dest, src, size);
  222. //u8* dest_ = (u8*) dest;
  223. //u8* src_ = (u8*) src;
  224. //for (u32 i = 0; i < size; i++) {
  225. // dest_[i] = src_[i];
  226. //}
  227. }
  228. // replace all instances of |c1| in |string| with |c2|
  229. static inline void replaceC(char* string, u32 length, char c1, char c2) {
  230. ULE_TYPES_H_FTAG;
  231. for (u32 i = 0; i < length; i++) {
  232. if (string[i] == c1) {
  233. string[i] = c2;
  234. }
  235. }
  236. }
  237. static inline const char* firstCharOccurence(const char* string, u32 length, char c) {
  238. ULE_TYPES_H_FTAG;
  239. for (u32 i = 0; i < length; i++) {
  240. const char* s = string + i;
  241. if (*s == c) {
  242. return s;
  243. }
  244. }
  245. return null;
  246. }
  247. static inline const char* firstCharOccurence(const char* string, char c) {
  248. ULE_TYPES_H_FTAG;
  249. return String::firstCharOccurence(string, String::len(string), c);
  250. }
  251. static inline const char* lastCharOccurence(const char* string, u32 length, char c) {
  252. ULE_TYPES_H_FTAG;
  253. for (s32 i = length - 1; i >= 0; i--) { // @NOTE 'i' needs to be a signed int here...
  254. if (*(string + i) == c) {
  255. return string + i;
  256. }
  257. }
  258. return null;
  259. }
  260. static inline const char* lastCharOccurence(const char* string, char c) {
  261. ULE_TYPES_H_FTAG;
  262. return String::lastCharOccurence(string, String::len(string), c);
  263. }
  264. static inline bool hasSuffix(const char* string, const char* suffix) {
  265. ULE_TYPES_H_FTAG;
  266. const char* p = String::lastCharOccurence(string, String::len(string), suffix[0]);
  267. if (p) return String::eq(p, suffix);
  268. return false;
  269. }
  270. static inline u32 countLines(const char* buffer) {
  271. ULE_TYPES_H_FTAG;
  272. u32 lines = 0;
  273. char c;
  274. while ((c = *buffer) != '\0') {
  275. if (c == '\n') lines++;
  276. buffer++;
  277. }
  278. return lines;
  279. }
  280. static inline bool isAscii(const char* buffer, u32 length) {
  281. ULE_TYPES_H_FTAG;
  282. const unsigned char* ubuffer = (const unsigned char*) buffer;
  283. for (u32 i = 0; i < length; i++) {
  284. if (ubuffer[i] & 128) { // binary: 0b 1000 0000
  285. return false;
  286. }
  287. }
  288. return true;
  289. }
  290. static inline bool isAsciiWhitespace(char c) {
  291. ULE_TYPES_H_FTAG;
  292. switch (c) {
  293. //case '\b':
  294. //case '\v':
  295. //case '\f':
  296. case '\r':
  297. case '\t':
  298. case '\n':
  299. case ' ':
  300. return true;
  301. default:
  302. return false;
  303. }
  304. }
  305. // static inline bool isUnicodeSpaceSeparator(wide character);
  306. // @TODO ALL OF THESE TRIMS
  307. //static inline char* trimStart(const char* str, u32 count);
  308. //static inline char* trimEnd(const char* str, u32 count);
  309. static inline char* trim(const char* str, u32 count, Allocator& allocator = Allocator::GetDefault()) {
  310. ULE_TYPES_H_FTAG;
  311. u32 length = String::len(str);
  312. if (length <= count) {
  313. return (char*) "";
  314. }
  315. char* buffer = (char*) allocator.mallocate(sizeof (char) * (length - 1), allocator.state);
  316. u32 i = 0;
  317. for (; i < (length - count); i++) {
  318. buffer[i] = str[i + 1];
  319. }
  320. buffer[i] = '\0';
  321. return buffer;
  322. }
  323. static inline char* asciiToLower(const char* str, Allocator& allocator = Allocator::GetDefault()) {
  324. ULE_TYPES_H_FTAG;
  325. u32 length = String::len(str);
  326. char* buffer = (char*) allocator.mallocate(sizeof (char) * length + 1, allocator.state);
  327. u32 i = 0;
  328. for (; i < length; i++) {
  329. buffer[i] = String::ASCII_LOWER[str[i]];
  330. }
  331. buffer[i] = '\0';
  332. return buffer;
  333. }
  334. static inline char* asciiToUpper(const char* str, Allocator& allocator = Allocator::GetDefault()) {
  335. ULE_TYPES_H_FTAG;
  336. u32 length = String::len(str);
  337. char* buffer = (char*) allocator.mallocate(sizeof (char) * length + 1, allocator.state);
  338. u32 i = 0;
  339. for (; i < length; i++) {
  340. buffer[i] = String::ASCII_LOWER[str[i]];
  341. }
  342. buffer[i] = '\0';
  343. return buffer;
  344. }
  345. static inline char* concat(const char* str1, const char* str2, Allocator& allocator = Allocator::GetDefault()) {
  346. ULE_TYPES_H_FTAG;
  347. u32 l1 = String::len(str1);
  348. u32 l2 = String::len(str2);
  349. u32 newLength = l1 + l2;
  350. char* newBuffer = (char*) allocator.mallocate(sizeof (char) * newLength + 1, allocator.state);
  351. u32 i = 0;
  352. for (; i < newLength; i++) {
  353. if (i < l1) {
  354. newBuffer[i] = str1[i];
  355. } else {
  356. newBuffer[i] = str2[i - l1];
  357. }
  358. }
  359. newBuffer[i] = '\0';
  360. return newBuffer;
  361. }
  362. static inline u32 write(char* dest, const char* src, u32 length) {
  363. ULE_TYPES_H_FTAG;
  364. u32 i = 0;
  365. for (; i < length; i++) {
  366. dest[i] = src[i];
  367. }
  368. dest[i] = '\0';
  369. return i;
  370. }
  371. // returns the number of characters written.
  372. static inline u32 write(char* dest, const char* src) {
  373. ULE_TYPES_H_FTAG;
  374. u32 length = String::len(src);
  375. return String::write(dest, src, length);
  376. }
  377. static inline char* read(const char* buffer, u32 length, Allocator& allocator = Allocator::GetDefault()) {
  378. ULE_TYPES_H_FTAG;
  379. char* tk = (char*) allocator.mallocate(sizeof (char) * length + 1, allocator.state);
  380. u32 i = 0;
  381. while (i < length) {
  382. tk[i] = *(buffer + i);
  383. i++;
  384. }
  385. tk[i] = '\0';
  386. return tk;
  387. }
  388. //---------------------------------------------------------------
  389. // Begin String class type
  390. static inline void SetStringClassAllocator(Allocator* allocator) {
  391. _stringClassAllocator = allocator;
  392. }
  393. char* Data; // Point to LocalBuf() or heap allocated
  394. int Capacity : 21; // Max 2 MB
  395. int LocalBufSize : 10; // Max 1023 bytes
  396. unsigned int Owned : 1; // Set when we have ownership of the pointed data (most common, unless using set_ref() method or StringRef constructor)
  397. inline char* c_str() { ULE_TYPES_H_FTAG; return Data; }
  398. inline const char* c_str() const { ULE_TYPES_H_FTAG; return Data; }
  399. inline bool empty() const { ULE_TYPES_H_FTAG; return Data[0] == 0; }
  400. inline int length() const { ULE_TYPES_H_FTAG; return (int)strlen(Data); } // by design, allow user to write into the buffer at any time
  401. inline int capacity() const { ULE_TYPES_H_FTAG; return Capacity; }
  402. inline bool owned() const { ULE_TYPES_H_FTAG; return Owned ? true : false; }
  403. inline char& operator[](size_t i) { ULE_TYPES_H_FTAG; return Data[i]; }
  404. inline char operator[](size_t i) const { ULE_TYPES_H_FTAG; return Data[i]; }
  405. inline String& operator=(const String& rhs) { ULE_TYPES_H_FTAG; set(rhs); return *this; }
  406. inline bool operator==(const String& rhs) const { ULE_TYPES_H_FTAG; return strcmp(c_str(), rhs.c_str()) == 0; }
  407. inline String& operator=(const char* rhs) { ULE_TYPES_H_FTAG; set(rhs); return *this; }
  408. inline bool operator==(const char* rhs) const { ULE_TYPES_H_FTAG; return strcmp(c_str(), rhs) == 0; }
  409. inline String() {
  410. ULE_TYPES_H_FTAG;
  411. Data = EmptyBuffer; // Shared READ-ONLY initial buffer for 0 capacity
  412. Capacity = 0;
  413. LocalBufSize = 0;
  414. Owned = 0;
  415. }
  416. inline String(const String& rhs) : String() {
  417. ULE_TYPES_H_FTAG;
  418. set(rhs);
  419. }
  420. inline String(const char* rhs) : String() {
  421. ULE_TYPES_H_FTAG;
  422. set(rhs);
  423. }
  424. inline void set_ref(const char* src) {
  425. ULE_TYPES_H_FTAG;
  426. if (Owned && !is_using_local_buf())
  427. _stringClassAllocator->free(Data, null);
  428. Data = src ? (char*)src : EmptyBuffer;
  429. Capacity = 0;
  430. Owned = 0;
  431. }
  432. inline void set(const String& src) {
  433. ULE_TYPES_H_FTAG;
  434. int buf_len = (int)strlen(src.c_str())+1;
  435. if ((int)Capacity < buf_len)
  436. reserve_discard(buf_len);
  437. memcpy(Data, (void*)src.c_str(), (size_t)buf_len);
  438. Owned = 1;
  439. }
  440. inline void set(const char* src) {
  441. ULE_TYPES_H_FTAG;
  442. // We allow set(NULL) or via = operator to clear the string.
  443. if (src == NULL)
  444. {
  445. clear();
  446. return;
  447. }
  448. int buf_len = (int)strlen(src)+1;
  449. if (Capacity < buf_len)
  450. reserve_discard(buf_len);
  451. memcpy(Data, (void*)src, (size_t)buf_len);
  452. Owned = 1;
  453. }
  454. inline void set(const char* src, const char* src_end) {
  455. ULE_TYPES_H_FTAG;
  456. STR_ASSERT(src != NULL && src_end >= src);
  457. int buf_len = (int)(src_end-src)+1;
  458. if ((int)Capacity < buf_len)
  459. reserve_discard(buf_len);
  460. memcpy(Data, (void*)src, (size_t)(buf_len - 1));
  461. Data[buf_len-1] = 0;
  462. Owned = 1;
  463. }
  464. // Clear
  465. inline void clear() {
  466. ULE_TYPES_H_FTAG;
  467. if (Owned && !is_using_local_buf())
  468. _stringClassAllocator->free(Data, null);
  469. if (LocalBufSize) {
  470. Data = local_buf();
  471. Data[0] = '\0';
  472. Capacity = LocalBufSize;
  473. Owned = 1;
  474. } else {
  475. Data = EmptyBuffer;
  476. Capacity = 0;
  477. Owned = 0;
  478. }
  479. }
  480. // Reserve memory, preserving the current of the buffer
  481. inline void reserve(int new_capacity) {
  482. ULE_TYPES_H_FTAG;
  483. if (new_capacity <= Capacity)
  484. return;
  485. char* new_data;
  486. if (new_capacity < LocalBufSize) {
  487. // Disowned -> LocalBuf
  488. new_data = local_buf();
  489. new_capacity = LocalBufSize;
  490. } else {
  491. // Disowned or LocalBuf -> Heap
  492. new_data = (char*)_stringClassAllocator->mallocate((size_t)new_capacity * sizeof(char), null);
  493. }
  494. // string in Data might be longer than new_capacity if it wasn't owned, don't copy too much
  495. #ifdef _MSC_VER
  496. strncpy_s(new_data, (size_t)new_capacity, Data, (size_t)new_capacity - 1);
  497. #else
  498. strncpy(new_data, Data, (size_t)new_capacity - 1);
  499. #endif
  500. new_data[new_capacity - 1] = 0;
  501. if (Owned && !is_using_local_buf())
  502. _stringClassAllocator->free(Data, null);
  503. Data = new_data;
  504. Capacity = new_capacity;
  505. Owned = 1;
  506. }
  507. // Reserve memory, discarding the current of the buffer (if we expect to be fully rewritten)
  508. inline void reserve_discard(int new_capacity) {
  509. ULE_TYPES_H_FTAG;
  510. if (new_capacity <= Capacity)
  511. return;
  512. if (Owned && !is_using_local_buf())
  513. _stringClassAllocator->free(Data, null);
  514. if (new_capacity < LocalBufSize) {
  515. // Disowned -> LocalBuf
  516. Data = local_buf();
  517. Capacity = LocalBufSize;
  518. } else {
  519. // Disowned or LocalBuf -> Heap
  520. Data = (char*)_stringClassAllocator->mallocate((size_t)new_capacity * sizeof(char), null);
  521. Capacity = new_capacity;
  522. }
  523. Owned = 1;
  524. }
  525. inline void shrink_to_fit() {
  526. ULE_TYPES_H_FTAG;
  527. if (!Owned || is_using_local_buf()) return;
  528. int new_capacity = length() + 1;
  529. if (Capacity <= new_capacity) return;
  530. char* new_data = (char*)_stringClassAllocator->mallocate((size_t)new_capacity * sizeof(char), null);
  531. memcpy(new_data, Data, (size_t)new_capacity);
  532. _stringClassAllocator->free(Data, null);
  533. Data = new_data;
  534. Capacity = new_capacity;
  535. }
  536. // FIXME: merge setfv() and appendfv()?
  537. inline int setfv(const char* fmt, va_list args) {
  538. ULE_TYPES_H_FTAG;
  539. // Needed for portability on platforms where va_list are passed by reference and modified by functions
  540. va_list args2;
  541. va_copy(args2, args);
  542. // First try
  543. int len = vsnprintf(Owned ? Data : NULL, Owned ? (size_t)Capacity : 0, fmt, args);
  544. STR_ASSERT(len >= 0);
  545. if (Capacity < len + 1)
  546. {
  547. reserve_discard(len + 1);
  548. len = vsnprintf(Data, (size_t)len + 1, fmt, args2);
  549. }
  550. STR_ASSERT(Owned);
  551. return len;
  552. }
  553. inline int setf(const char* fmt, ...) {
  554. ULE_TYPES_H_FTAG;
  555. va_list args;
  556. va_start(args, fmt);
  557. int len = setfv(fmt, args);
  558. va_end(args);
  559. return len;
  560. }
  561. inline int setfv_nogrow(const char* fmt, va_list args) {
  562. ULE_TYPES_H_FTAG;
  563. STR_ASSERT(Owned);
  564. if (Capacity == 0) return 0;
  565. int w = vsnprintf(Data, (size_t)Capacity, fmt, args);
  566. Data[Capacity - 1] = 0;
  567. Owned = 1;
  568. return (w == -1) ? Capacity - 1 : w;
  569. }
  570. inline int setf_nogrow(const char* fmt, ...) {
  571. ULE_TYPES_H_FTAG;
  572. va_list args;
  573. va_start(args, fmt);
  574. int len = setfv_nogrow(fmt, args);
  575. va_end(args);
  576. return len;
  577. }
  578. inline int append_from(int idx, char c) {
  579. ULE_TYPES_H_FTAG;
  580. int add_len = 1;
  581. if (Capacity < idx + add_len + 1)
  582. reserve(idx + add_len + 1);
  583. Data[idx] = c;
  584. Data[idx + add_len] = 0;
  585. STR_ASSERT(Owned);
  586. return add_len;
  587. }
  588. inline int append_from(int idx, const char* s, const char* s_end) {
  589. ULE_TYPES_H_FTAG;
  590. if (!s_end) s_end = s + strlen(s);
  591. int add_len = (int)(s_end - s);
  592. if (Capacity < idx + add_len + 1) reserve(idx + add_len + 1);
  593. memcpy(Data + idx, (void*)s, (size_t)add_len);
  594. Data[idx + add_len] = 0; // Our source data isn't necessarily zero-terminated
  595. STR_ASSERT(Owned);
  596. return add_len;
  597. }
  598. // FIXME: merge setfv() and appendfv()?
  599. inline int appendfv_from(int idx, const char* fmt, va_list args) {
  600. ULE_TYPES_H_FTAG;
  601. // Needed for portability on platforms where va_list are passed by reference and modified by functions
  602. va_list args2;
  603. va_copy(args2, args);
  604. // First try
  605. int add_len = vsnprintf(Owned ? Data + idx : NULL, Owned ? (size_t)(Capacity - idx) : 0, fmt, args);
  606. STR_ASSERT(add_len >= 0);
  607. if (Capacity < idx + add_len + 1) {
  608. reserve(idx + add_len + 1);
  609. add_len = vsnprintf(Data + idx, (size_t)add_len + 1, fmt, args2);
  610. }
  611. STR_ASSERT(Owned);
  612. return add_len;
  613. }
  614. inline int appendf_from(int idx, const char* fmt, ...) {
  615. ULE_TYPES_H_FTAG;
  616. va_list args;
  617. va_start(args, fmt);
  618. int len = appendfv_from(idx, fmt, args);
  619. va_end(args);
  620. return len;
  621. }
  622. inline int append(char c) {
  623. ULE_TYPES_H_FTAG;
  624. int cur_len = length();
  625. return append_from(cur_len, c);
  626. }
  627. inline int append(const char* s, const char* s_end = null) {
  628. ULE_TYPES_H_FTAG;
  629. int cur_len = length();
  630. return append_from(cur_len, s, s_end);
  631. }
  632. inline int appendfv(const char* fmt, va_list args) {
  633. ULE_TYPES_H_FTAG;
  634. int cur_len = length();
  635. return appendfv_from(cur_len, fmt, args);
  636. }
  637. int appendf(const char* fmt, ...) {
  638. ULE_TYPES_H_FTAG;
  639. va_list args;
  640. va_start(args, fmt);
  641. int len = appendfv(fmt, args);
  642. va_end(args);
  643. return len;
  644. }
  645. // Destructor for all variants
  646. inline ~String()
  647. {
  648. if (Owned && !is_using_local_buf())
  649. if (_stringClassAllocator->free != null) ::_stringClassAllocator->free(Data, null);
  650. }
  651. protected:
  652. inline char* local_buf() { ULE_TYPES_H_FTAG; return (char*)this + sizeof(String); }
  653. inline const char* local_buf() const { ULE_TYPES_H_FTAG; return (char*)this + sizeof(String); }
  654. inline bool is_using_local_buf() const { ULE_TYPES_H_FTAG; return Data == local_buf() && LocalBufSize != 0; }
  655. // Constructor for StringXXX variants with local buffer
  656. String(unsigned short local_buf_size) {
  657. ULE_TYPES_H_FTAG;
  658. STR_ASSERT(local_buf_size < 1024);
  659. Data = local_buf();
  660. Data[0] = '\0';
  661. Capacity = local_buf_size;
  662. LocalBufSize = local_buf_size;
  663. Owned = 1;
  664. }
  665. };
  666. // Literal/reference string
  667. class StringRef : public String {
  668. public:
  669. StringRef(const char* s) : String() { ULE_TYPES_H_FTAG; set_ref(s); }
  670. };
  671. // Types embedding a local buffer
  672. // NB: we need to override the constructor and = operator for both String& and TYPENAME (without the later compiler will call a default copy operator)
  673. #define STR_DEFINETYPE(TYPENAME, LOCALBUFSIZE) \
  674. class TYPENAME : public String \
  675. { \
  676. char local_buf[LOCALBUFSIZE]; \
  677. public: \
  678. TYPENAME() : String(LOCALBUFSIZE) {} \
  679. TYPENAME(const String& rhs) : String(LOCALBUFSIZE) { ULE_TYPES_H_FTAG; set(rhs); } \
  680. TYPENAME(const char* rhs) : String(LOCALBUFSIZE) { ULE_TYPES_H_FTAG; set(rhs); } \
  681. TYPENAME(const TYPENAME& rhs) : String(LOCALBUFSIZE) { ULE_TYPES_H_FTAG; set(rhs); } \
  682. TYPENAME& operator=(const char* rhs) { ULE_TYPES_H_FTAG; set(rhs); return *this; } \
  683. TYPENAME& operator=(const String& rhs) { ULE_TYPES_H_FTAG; set(rhs); return *this; } \
  684. TYPENAME& operator=(const TYPENAME& rhs) { ULE_TYPES_H_FTAG; set(rhs); return *this; } \
  685. };
  686. // 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)
  687. // -V:STR_DEFINETYPE:730
  688. // Helper to define StringXXXf constructors
  689. #define STR_DEFINETYPE_F(TYPENAME, TYPENAME_F) \
  690. class TYPENAME_F : public TYPENAME \
  691. { \
  692. public: \
  693. TYPENAME_F(const char* fmt, ...) : TYPENAME() { ULE_TYPES_H_FTAG; va_list args; va_start(args, fmt); setfv(fmt, args); va_end(args); } \
  694. };
  695. #ifdef __clang__
  696. #pragma clang diagnostic push
  697. #pragma clang diagnostic ignored "-Wunused-private-field" // warning : private field 'local_buf' is not used
  698. #endif
  699. // Declaring types for common sizes here
  700. STR_DEFINETYPE(String16, 16)
  701. STR_DEFINETYPE(String30, 30)
  702. STR_DEFINETYPE(String64, 64)
  703. STR_DEFINETYPE(String128, 128)
  704. STR_DEFINETYPE(String256, 256)
  705. STR_DEFINETYPE(String512, 512)
  706. // Declaring helper constructors to pass in format strings in one statement
  707. STR_DEFINETYPE_F(String16, String16f)
  708. STR_DEFINETYPE_F(String30, String30f)
  709. STR_DEFINETYPE_F(String64, String64f)
  710. STR_DEFINETYPE_F(String128, String128f)
  711. STR_DEFINETYPE_F(String256, String256f)
  712. STR_DEFINETYPE_F(String512, String512f)
  713. #if STR_DEFINE_STR32
  714. STR_DEFINETYPE(String32, 32)
  715. STR_DEFINETYPE_F(String32, String32f)
  716. #endif
  717. // by default, if you use a string type with a local buffer, and append to it with something bigger than the local buffer,
  718. // you end up realloc'ing space to fit, and this reallocation is stingy - only exactly as much as needed.
  719. // if you are creating a string which may be repeatedly concatenated with other strings/appended to, it's better
  720. // to do a smaller number of growing allocations along the lines of what dynamic arrays do.
  721. // this type is the same as other string types, but if it has to reserve additional space for itself, it will do so
  722. // eagerly, at a rate of 1.5x
  723. #define STRINGBUFFER_DEFINETYPE(TYPENAME, LOCALBUFSIZE) \
  724. class TYPENAME : public String { \
  725. char local_buf[LOCALBUFSIZE]; \
  726. public: \
  727. TYPENAME(const char* fmt, ...) : String(LOCALBUFSIZE) { ULE_TYPES_H_FTAG; va_list args; va_start(args, fmt); setfv(fmt, args); va_end(args); } \
  728. TYPENAME() : String(LOCALBUFSIZE) { ULE_TYPES_H_FTAG; } \
  729. TYPENAME(const String& rhs) : String(LOCALBUFSIZE) { ULE_TYPES_H_FTAG; set(rhs); } \
  730. TYPENAME(const char* rhs) : String(LOCALBUFSIZE) { ULE_TYPES_H_FTAG; set(rhs); } \
  731. TYPENAME(const TYPENAME& rhs) : String(LOCALBUFSIZE) { ULE_TYPES_H_FTAG; set(rhs); } \
  732. TYPENAME& operator=(const char* rhs) { ULE_TYPES_H_FTAG; set(rhs); return *this; } \
  733. TYPENAME& operator=(const String& rhs) { ULE_TYPES_H_FTAG; set(rhs); return *this; } \
  734. TYPENAME& operator=(const TYPENAME& rhs) { ULE_TYPES_H_FTAG; set(rhs); return *this; } \
  735. void reserve(int new_capacity) { \
  736. ULE_TYPES_H_FTAG; \
  737. if (new_capacity <= Capacity) \
  738. return; \
  739. char* new_data; \
  740. if (new_capacity < LocalBufSize) { \
  741. new_data = (char*)this + sizeof(String); \
  742. new_capacity = LocalBufSize; \
  743. } else { \
  744. new_data = (char*)_stringClassAllocator->mallocate((size_t)new_capacity * sizeof(char), null); \
  745. } \
  746. strncpy(new_data, Data, (size_t)new_capacity - 1); \
  747. new_data[new_capacity - 1] = 0; \
  748. if (Owned && !is_using_local_buf()) \
  749. _stringClassAllocator->free(Data, null); \
  750. Data = new_data; \
  751. Capacity = new_capacity; \
  752. Owned = 1; \
  753. } \
  754. void reserve_discard(int new_capacity) { \
  755. ULE_TYPES_H_FTAG; \
  756. if (new_capacity <= Capacity) \
  757. return; \
  758. if (Owned && !is_using_local_buf()) \
  759. _stringClassAllocator->free(Data, null); \
  760. if (new_capacity < LocalBufSize) { \
  761. Data = (char*)this + sizeof(String); \
  762. Capacity = LocalBufSize; \
  763. } else { \
  764. while (Capacity < new_capacity) { \
  765. Capacity = (s32)(Capacity * 1.5f); \
  766. Data = (char*) _stringClassAllocator->mallocate((size_t) Capacity * sizeof(char), null); \
  767. } \
  768. } \
  769. Owned = 1; \
  770. } \
  771. };
  772. STRINGBUFFER_DEFINETYPE(StringBuffer512, 512)
  773. #ifdef __clang__
  774. #pragma clang diagnostic pop
  775. #endif
  776. // On some platform vsnprintf() takes va_list by reference and modifies it.
  777. // va_copy is the 'correct' way to copy a va_list but Visual Studio prior to 2013 doesn't have it.
  778. #ifndef va_copy
  779. #define va_copy(dest, src) (dest = src)
  780. #endif
  781. //-------------------------------------------------------------------------
  782. // https://www.fileformat.info/info/unicode/category/Zs/list.htm
  783. /* @TODO
  784. bool isUnicodeSpaceSeparator(char c) {
  785. switch (c) {
  786. case 0x20:
  787. case 0xA0:
  788. case 0x1680:
  789. case 0x2000:
  790. case 0x2001:
  791. case 0x2002:
  792. case 0x2003:
  793. case 0x2004:
  794. case 0x2005:
  795. case 0x2006:
  796. case 0x2007:
  797. case 0x2008:
  798. case 0x2009:
  799. case 0x200A:
  800. case 0x202F:
  801. case 0x205F:
  802. case 0x3000:
  803. return true;
  804. default:
  805. return false;
  806. }
  807. }
  808. */
  809. //struct StringBuffer {
  810. // u32 length;
  811. // u32 capacity;
  812. // char* data;
  813. //
  814. // StringBuffer(u32 initialSize = 2048);
  815. // StringBuffer(const char* string);
  816. // ~StringBuffer();
  817. //
  818. // void checkIfShouldGrow();
  819. // bool isEmpty() const;
  820. // bool isFull() const;
  821. // char pop();
  822. // u32 append(char e);
  823. //};
  824. ////================================================================================
  825. //StringBuffer::StringBuffer(u32 initialSize) {
  826. // ULE_TYPES_H_FTAG;
  827. // this->length = 0;
  828. // this->capacity = initialSize;
  829. // this->data = (char*) pMalloc(sizeof(char) * this->capacity);
  830. //}
  831. //
  832. //StringBuffer::StringBuffer(const char* string) {
  833. // this->length = String::len(string);
  834. // this->capacity = this->length;
  835. // this->data = cpy(string, this->length);
  836. //}
  837. //
  838. //StringBuffer::~StringBuffer() {
  839. // pFree(this->data);
  840. //}
  841. //
  842. //void StringBuffer::checkIfShouldGrow() {
  843. // ULE_TYPES_H_FTAG;
  844. // if (this->isFull()) {
  845. // // optimal number as you approach infinite elements approaches PHI, but 1.5 sometimes works better for finite sizes
  846. // // more testing is probably needed
  847. // this->capacity = (u32) (this->capacity * 1.5);
  848. // this->data = (char*) pRealloc(this->data, sizeof(char) * this->capacity);
  849. // }
  850. //}
  851. //
  852. //bool StringBuffer::isEmpty() const {
  853. // ULE_TYPES_H_FTAG;
  854. // return this->length == 0;
  855. //}
  856. //
  857. //bool StringBuffer::isFull() const {
  858. // ULE_TYPES_H_FTAG;
  859. // return this->length == this->capacity;
  860. //}
  861. //
  862. //char StringBuffer::pop() {
  863. // ULE_TYPES_H_FTAG;
  864. // if (this->isEmpty()) {
  865. // die("empty");
  866. // }
  867. //
  868. // return this->data[--this->length];
  869. //}
  870. //
  871. //u32 StringBuffer::append(char e) {
  872. // ULE_TYPES_H_FTAG;
  873. // this->checkIfShouldGrow();
  874. //
  875. // this->data[this->length++] = e;
  876. //
  877. // return this->length - 1;
  878. //}
  879. #endif