string.cpp 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407
  1. gb_global gbArena string_buffer_arena = {};
  2. gb_global gbAllocator string_buffer_allocator = {};
  3. void init_string_buffer_memory() {
  4. // NOTE(bill): This should be enough memory for file systems
  5. gb_arena_init_from_allocator(&string_buffer_arena, gb_heap_allocator(), gb_megabytes(1));
  6. string_buffer_allocator = gb_arena_allocator(&string_buffer_arena);
  7. }
  8. // NOTE(bill): Used for UTF-8 strings
  9. typedef struct String {
  10. u8 * text;
  11. isize len;
  12. } String;
  13. // NOTE(bill): used for printf style arguments
  14. #define LIT(x) (x).len, (x).text
  15. typedef struct String16 {
  16. wchar_t *text;
  17. isize len;
  18. } String16;
  19. gb_inline String make_string(u8 *text, isize len) {
  20. String s;
  21. s.text = text;
  22. if (len < 0) {
  23. len = gb_strlen(cast(char *)text);
  24. }
  25. s.len = len;
  26. return s;
  27. }
  28. gb_inline String16 make_string16(wchar_t *text, isize len) {
  29. String16 s;
  30. s.text = text;
  31. s.len = len;
  32. return s;
  33. }
  34. gb_inline String make_string(char *text) {
  35. return make_string(cast(u8 *)cast(void *)text, gb_strlen(text));
  36. }
  37. gb_inline b32 are_strings_equal(String a, String b) {
  38. if (a.len == b.len) {
  39. return gb_memcompare(a.text, b.text, a.len) == 0;
  40. }
  41. return false;
  42. }
  43. gb_inline b32 are_strings_equal_ignore_case(String a, String b) {
  44. if (a.len == b.len) {
  45. for (isize i = 0; i < a.len; i++) {
  46. char x = cast(char)a.text[i];
  47. char y = cast(char)b.text[i];
  48. if (gb_char_to_lower(x) != gb_char_to_lower(y))
  49. return false;
  50. }
  51. return true;
  52. }
  53. return false;
  54. }
  55. int string_compare(String x, String y) {
  56. if (x.len == y.len &&
  57. x.text == y.text) {
  58. return 0;
  59. }
  60. isize n = gb_min(x.len, y.len);
  61. isize fast = n/gb_size_of(isize) + 1;
  62. isize offset = (fast-1)*gb_size_of(isize);
  63. isize curr_block = 0;
  64. if (n <= gb_size_of(isize)) {
  65. fast = 0;
  66. }
  67. isize *la = cast(isize *)x.text;
  68. isize *lb = cast(isize *)y.text;
  69. for (; curr_block < fast; curr_block++) {
  70. if (la[curr_block] ^ lb[curr_block]) {
  71. for (isize pos = curr_block*gb_size_of(isize); pos < n; pos++) {
  72. if (x.text[pos] ^ y.text[pos]) {
  73. return cast(int)x.text[pos] - cast(int)y.text[pos];
  74. }
  75. }
  76. }
  77. }
  78. for (; offset < n; offset++) {
  79. if (x.text[offset] ^ y.text[offset]) {
  80. return cast(int)x.text[offset] - cast(int)y.text[offset];
  81. }
  82. }
  83. return 0;
  84. }
  85. GB_COMPARE_PROC(string_cmp_proc) {
  86. String x = *(String *)a;
  87. String y = *(String *)b;
  88. return string_compare(x, y);
  89. }
  90. bool operator ==(String a, String b) { return are_strings_equal(a, b) != 0; }
  91. bool operator !=(String a, String b) { return !operator==(a, b); }
  92. bool operator < (String a, String b) { return string_compare(a, b) < 0; }
  93. bool operator > (String a, String b) { return string_compare(a, b) > 0; }
  94. bool operator <=(String a, String b) { return string_compare(a, b) <= 0; }
  95. bool operator >=(String a, String b) { return string_compare(a, b) >= 0; }
  96. gb_inline isize string_extension_position(String str) {
  97. isize dot_pos = -1;
  98. isize i = str.len;
  99. b32 seen_dot = false;
  100. while (i --> 0) {
  101. if (str.text[i] == GB_PATH_SEPARATOR)
  102. break;
  103. if (str.text[i] == '.') {
  104. dot_pos = i;
  105. break;
  106. }
  107. }
  108. return dot_pos;
  109. }
  110. gb_inline b32 string_has_extension(String str, String ext) {
  111. if (str.len > ext.len+1) {
  112. u8 *s = str.text+str.len - ext.len-1;
  113. if (s[0] == '.') {
  114. s++;
  115. return gb_memcompare(s, ext.text, ext.len) == 0;
  116. }
  117. return false;
  118. }
  119. return false;
  120. }
  121. b32 string_contains_char(String s, u8 c) {
  122. for (isize i = 0; i < s.len; i++) {
  123. if (s.text[i] == c)
  124. return true;
  125. }
  126. return false;
  127. }
  128. // TODO(bill): Make this non-windows specific
  129. String16 string_to_string16(gbAllocator a, String s) {
  130. if (s.len < 1) {
  131. return make_string16(NULL, 0);
  132. }
  133. int len = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS,
  134. cast(char *)s.text, s.len, NULL, 0);
  135. if (len == 0) {
  136. return make_string16(NULL, 0);
  137. }
  138. wchar_t *text = gb_alloc_array(a, wchar_t, len+1);
  139. int len1 = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS,
  140. cast(char *)s.text, s.len, text, len);
  141. if (len1 == 0) {
  142. gb_free(a, text);
  143. return make_string16(NULL, 0);
  144. }
  145. text[len] = 0;
  146. return make_string16(text, len-1);
  147. }
  148. String string16_to_string(gbAllocator a, String16 s) {
  149. if (s.len < 1) {
  150. return make_string(NULL, 0);
  151. }
  152. int len = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS,
  153. s.text, s.len, NULL, 0,
  154. NULL, NULL);
  155. if (len == 0) {
  156. return make_string(NULL, 0);
  157. }
  158. u8 *text = gb_alloc_array(a, u8, len+1);
  159. int len1 = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS,
  160. s.text, s.len, cast(char *)text, len,
  161. NULL, NULL);
  162. if (len1 == 0) {
  163. gb_free(a, text);
  164. return make_string(NULL, 0);
  165. }
  166. text[len] = 0;
  167. return make_string(text, len-1);
  168. }
  169. b32 unquote_char(String s, u8 quote, Rune *rune, b32 *multiple_bytes, String *tail_string) {
  170. if (s.text[0] == quote &&
  171. (quote == '$' || quote == '"')) {
  172. return false;
  173. } else if (s.text[0] >= 0x80) {
  174. Rune r = -1;
  175. isize size = gb_utf8_decode(s.text, s.len, &r);
  176. *rune = r;
  177. *multiple_bytes = true;
  178. *tail_string = make_string(s.text+size, s.len-size);
  179. return true;
  180. } else if (s.text[0] != '\\') {
  181. *rune = s.text[0];
  182. *tail_string = make_string(s.text+1, s.len-1);
  183. return true;
  184. }
  185. if (s.len <= 1) {
  186. return false;
  187. }
  188. u8 c = s.text[1];
  189. s = make_string(s.text+2, s.len-2);
  190. switch (c) {
  191. default: return false;
  192. case 'a': *rune = '\a'; break;
  193. case 'b': *rune = '\b'; break;
  194. case 'f': *rune = '\f'; break;
  195. case 'n': *rune = '\n'; break;
  196. case 'r': *rune = '\r'; break;
  197. case 't': *rune = '\t'; break;
  198. case 'v': *rune = '\v'; break;
  199. case '\\': *rune = '\\'; break;
  200. case '$':
  201. case '"':
  202. if (c != quote) {
  203. return false;
  204. }
  205. *rune = c;
  206. break;
  207. case '0':
  208. case '1':
  209. case '2':
  210. case '3':
  211. case '4':
  212. case '5':
  213. case '6':
  214. case '7': {
  215. i32 r = gb_digit_to_int(c);
  216. if (s.len < 2) {
  217. return false;
  218. }
  219. for (isize i = 0; i < 2; i++) {
  220. i32 d = gb_digit_to_int(s.text[i]);
  221. if (d < 0 || d > 7) {
  222. return false;
  223. }
  224. r = (r<<3) | d;
  225. }
  226. s = make_string(s.text+2, s.len-2);
  227. if (r > 0xff) {
  228. return false;
  229. }
  230. *rune = r;
  231. } break;
  232. case 'x':
  233. case 'u':
  234. case 'U': {
  235. isize count = 0;
  236. switch (c) {
  237. case 'x': count = 2; break;
  238. case 'u': count = 4; break;
  239. case 'U': count = 8; break;
  240. }
  241. Rune r = 0;
  242. if (s.len < count) {
  243. return false;
  244. }
  245. for (isize i = 0; i < count; i++) {
  246. i32 d = gb_hex_digit_to_int(s.text[i]);
  247. if (d < 0) {
  248. return false;
  249. }
  250. r = (r<<4) | d;
  251. }
  252. s = make_string(s.text+count, s.len-count);
  253. if (c == 'x') {
  254. *rune = r;
  255. break;
  256. }
  257. if (r > GB_RUNE_MAX) {
  258. return false;
  259. }
  260. *rune = r;
  261. *multiple_bytes = true;
  262. } break;
  263. }
  264. *tail_string = s;
  265. return true;
  266. }
  267. // 0 == failure
  268. // 1 == original memory
  269. // 2 == new allocation
  270. i32 unquote_string(gbAllocator a, String *s_) {
  271. GB_ASSERT(s_ != NULL);
  272. String s = *s_;
  273. isize n = s.len;
  274. if (n < 2)
  275. return 0;
  276. u8 quote = s.text[0];
  277. if (quote != s.text[n-1])
  278. return 0;
  279. s.text += 1;
  280. s.len -= 2;
  281. if (quote == '`') {
  282. if (string_contains_char(s, '`')) {
  283. return 0;
  284. }
  285. *s_ = s;
  286. return 1;
  287. }
  288. if (quote != '"' && quote != '$')
  289. return 0;
  290. if (string_contains_char(s, '\n'))
  291. return 0;
  292. if (!string_contains_char(s, '\\') && !string_contains_char(s, quote)) {
  293. if (quote == '"') {
  294. *s_ = s;
  295. return 1;
  296. } else if (quote == '$') {
  297. Rune r = GB_RUNE_INVALID;
  298. isize size = gb_utf8_decode(s.text, s.len, &r);
  299. if ((size == s.len) && (r != -1 || size != 1)) {
  300. *s_ = s;
  301. return 1;
  302. }
  303. }
  304. }
  305. u8 rune_temp[4] = {};
  306. isize buf_len = 3*s.len / 2;
  307. u8 *buf = gb_alloc_array(a, u8, buf_len);
  308. isize offset = 0;
  309. while (s.len > 0) {
  310. String tail_string = {};
  311. Rune r = 0;
  312. b32 multiple_bytes = false;
  313. b32 success = unquote_char(s, quote, &r, &multiple_bytes, &tail_string);
  314. if (!success) {
  315. gb_free(a, buf);
  316. return 0;
  317. }
  318. s = tail_string;
  319. if (r < 0x80 || !multiple_bytes) {
  320. buf[offset++] = cast(u8)r;
  321. } else {
  322. isize size = gb_utf8_encode_rune(rune_temp, r);
  323. gb_memcopy(buf+offset, rune_temp, size);
  324. offset += size;
  325. }
  326. if (quote == '$' && s.len != 0) {
  327. gb_free(a, buf);
  328. return 0;
  329. }
  330. }
  331. *s_ = make_string(buf, offset);
  332. return 2;
  333. }