advent-of-code

Solutions for Advent of Code.
git clone git://git.amin.space/advent-of-code.git
Log | Files | Refs | LICENSE

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