am_string.h (6375B)
1 #ifndef AM_STRING_H 2 #define AM_STRING_H 3 4 #ifndef AM_MEMORY_H 5 #error "am_memory.h is required" 6 #endif 7 #ifndef AM_LIST_H 8 #error "am_list.h is required" 9 #endif 10 11 typedef struct { 12 usz size; 13 u8 *str; 14 } Str; 15 16 typedef struct StrListNode StrListNode; 17 struct StrListNode { 18 StrListNode *next; 19 Str s; 20 }; 21 22 typedef struct { 23 StrListNode *first; 24 StrListNode *last; 25 u64 node_count; 26 u64 total_size; 27 } StrList; 28 29 #define AM_STR_LIT(s) (Str) { .str = (u8 *)(s), .size = sizeof(s) - 1, } 30 #define AM_STR_EXPAND(s) (s32)((s).size), ((s).str) 31 32 internal usz am_cstr_len(char *cstr) { 33 usz len = 0; 34 while (cstr && *cstr != '\0') { 35 len++; 36 cstr++; 37 } 38 return len; 39 } 40 41 internal Str am_str(u8 *str, usz size) { 42 return (Str) { .size = size, .str = str, }; 43 } 44 45 internal Str am_str_from_range(u8 *start, u8 *opl) { 46 assert(start < opl); 47 return am_str(start, opl - start); 48 } 49 50 internal Str am_str_from_cstr(char *cstr, usz len) { 51 return am_str((u8 *)cstr, len); 52 } 53 54 // TODO: Use string interning 55 internal bool am_str_eq(Str s1, Str s2) { 56 if (s1.size != s2.size) { 57 return false; 58 } 59 60 for (usz i = 0; i < s1.size; i++) { 61 if (s1.str[i] != s2.str[i]) { 62 return false; 63 } 64 } 65 66 return true; 67 } 68 69 internal bool am_cstr_eq(char *cstr1, char *cstr2) { 70 usz len1 = am_cstr_len(cstr1); 71 usz len2 = am_cstr_len(cstr2); 72 return am_str_eq(am_str_from_cstr(cstr1, len1), am_str_from_cstr(cstr2, len2)); 73 } 74 75 internal Str am_str_line(Str s, u8 *cursor) { 76 u8 *opl = s.str + s.size; 77 78 assert(cursor >= s.str && cursor < opl); 79 80 u8 *end = cursor; 81 while (end < opl) { 82 if (*end == '\n') { 83 break; 84 } 85 end++; 86 } 87 88 u8 *beginning = cursor - (*cursor == '\n'); 89 while (beginning >= s.str) { 90 if (*beginning == '\n') { 91 beginning++; 92 break; 93 } 94 beginning--; 95 } 96 97 return am_str_from_range(beginning, end); 98 } 99 100 internal bool am_str_contains(Str s, char c) { 101 for (usz i = 0; i < s.size; i++) { 102 if (s.str[i] == c) { 103 return true; 104 } 105 } 106 return false; 107 } 108 109 internal bool am_cstr_contains(char *s, usz len, char c) { 110 return am_str_contains(am_str_from_cstr(s, len), c); 111 } 112 113 internal bool am_str_cstr_eq(Str s, char *cstr) { 114 return am_str_eq(s, am_str_from_cstr(cstr, am_cstr_len(cstr))); 115 } 116 117 internal void am_str_list_append(MemArena *arena, StrList *list, Str s) { 118 StrListNode *node = am_mem_arena_push(arena, sizeof(StrListNode)); 119 *node = (StrListNode) { 120 .s = s, 121 }; 122 SLL_QUEUE_PUSH(list->first, list->last, node); 123 list->node_count += 1; 124 list->total_size += s.size; 125 } 126 127 typedef struct { 128 Str pre; 129 Str mid; 130 Str post; 131 } StringJoinOptions; 132 133 internal Str am_str_join(MemArena *arena, StrList *list, StringJoinOptions *options) { 134 StringJoinOptions join; 135 if (options) { 136 join = *options; 137 } else { 138 join = (StringJoinOptions){0}; 139 } 140 141 u64 total_size = list->total_size 142 + join.pre.size 143 + (join.mid.size * (list->node_count - 1)) 144 + join.post.size; 145 146 Str s= { 147 .str = AM_MEM_ARENA_PUSH_ARRAY(arena, u8, total_size), 148 .size = total_size, 149 }; 150 151 u8 *p = s.str; 152 am_mem_copy(p, join.pre.str, join.pre.size); 153 p += join.pre.size; 154 155 bool is_mid = false; 156 for (StrListNode *n = list->first; n; n = n->next) { 157 if (is_mid) { 158 am_mem_copy(p, join.mid.str, join.mid.size); 159 p += join.mid.size; 160 } 161 162 am_mem_copy(p, n->s.str, n->s.size); 163 p += n->s.size; 164 165 is_mid = true; 166 } 167 168 am_mem_copy(p, join.post.str, join.post.size); 169 p += join.post.size; 170 171 return s; 172 } 173 174 internal StrList am_str_split(MemArena *arena, Str s, u8 *split_chars, u64 split_char_count) { 175 StrList list = {0}; 176 177 u8 *cursor = s.str; 178 u8 *split_beginning = cursor; 179 u8 *opl = s.str + s.size; 180 while (cursor < opl) { 181 bool split_byte = false; 182 for (u64 i = 0; i < split_char_count; i++) { 183 if (split_chars[i] == *cursor) { 184 split_byte = true; 185 break; 186 } 187 } 188 189 if (split_byte) { 190 if (split_beginning < cursor) { 191 am_str_list_append(arena, &list, am_str_from_range(split_beginning, cursor)); 192 } 193 split_beginning = cursor + 1; 194 } 195 cursor++; 196 } 197 198 if (split_beginning < cursor) { 199 am_str_list_append(arena, &list, am_str_from_range(split_beginning, cursor)); 200 } 201 202 return list; 203 } 204 205 #if defined(AM_INCLUDE_TESTS) 206 internal void am_string_test(void) { 207 assert(am_cstr_len("abcdefg") == 7); 208 assert(am_cstr_len("") == 0); 209 assert(am_cstr_len("\0") == 0); 210 assert(am_cstr_eq("", "")); 211 assert(am_cstr_eq("\0", "\0")); 212 assert(am_cstr_eq("abc", "abc")); 213 assert(!am_cstr_eq("ABC", "abc")); 214 assert(!am_cstr_eq("", " ")); 215 assert(!am_cstr_eq("abc", "abcde")); 216 assert(!am_str_cstr_eq(AM_STR_LIT("abcd"), "abc")); 217 assert(am_str_cstr_eq(AM_STR_LIT("abc"), "abc")); 218 assert(am_cstr_contains("abc", 3, 'c')); 219 assert(am_cstr_contains("a c", 3, ' ')); 220 assert(am_cstr_contains("", 1, '\0')); 221 assert(!am_cstr_contains("abc", 3, 'z')); 222 assert(!am_cstr_contains("", 0, 'z')); 223 assert(!am_cstr_contains("", 0, '\0')); 224 assert(am_cstr_contains("https://www.example.com", 23, '/')); 225 226 { 227 StrList l = {0}; 228 MemArena a = am_mem_arena_create(am_mem_base_allocator_malloc()); 229 Str strings[] = { 230 AM_STR_LIT("one"), 231 AM_STR_LIT("two"), 232 AM_STR_LIT("three"), 233 }; 234 235 for (u64 i = 0; i < ARRAY_COUNT(strings); i++) { 236 am_str_list_append(&a, &l, strings[i]); 237 } 238 239 Str joined = am_str_join(&a, &l, &(StringJoinOptions){ 240 .pre = AM_STR_LIT("Joined: '"), 241 .mid = AM_STR_LIT(", "), 242 .post = AM_STR_LIT("'."), 243 }); 244 245 printf("%.*s\n", AM_STR_EXPAND(joined)); 246 247 StrList split = am_str_split(&a, AM_STR_LIT(", one, two, three, "), (u8 *)", ", 2); 248 249 for (StrListNode *n = split.first; n; n = n->next) { 250 printf("split: '%.*s'\n", AM_STR_EXPAND(n->s)); 251 } 252 am_mem_arena_release(&a); 253 } 254 } 255 #endif // defined(AM_INCLUDE_TESTS) 256 257 #endif // AM_STRING_H