advent-of-code-2024/aoc/str.h

208 lines
4.9 KiB
C

#ifndef STR_H
#define STR_H
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
typedef struct {
uint8_t *data;
size_t len;
} str;
typedef struct {
bool found;
size_t offset;
} str_find_result;
#define STR(s) (str) { .data = (uint8_t *) s, .len = sizeof(s) - 1 }
str make_str(uint8_t *data, size_t len);
str cstr(const char *cstr);
str str_next_token(str *source, str delim);
str str_sub(str s, size_t start, size_t end);
str str_trim_left(str s);
str str_trim_right(str s);
str str_trim(str s);
bool str_eq(str a, str b);
bool str_starts_with(str s, str prefix);
bool str_ends_with(str s, str suffix);
bool str_empty(str s);
void str_split(str s, str delim, str *first, str *second);
str_find_result str_find_left(str haystack, str needle);
str_find_result str_find_right(str haystack, str needle);
void str_print(str s);
#define STR_FMT "%.*s"
#define STR_ARG(s) (int) (s).len, (const char *) (s).data
#ifdef STR_IMPLEMENTATION
#include <stdio.h>
str make_str(uint8_t *data, size_t len) {
return (str) { .data = data, .len = len };
}
str cstr(const char *cstr) {
size_t len = 0;
for (const char *it = cstr; *it; ++it) {
len++;
}
return (str) {
.data = (uint8_t *) cstr,
.len = len
};
}
str str_sub(str s, size_t start, size_t end) {
return (str) {
.data = s.data + start,
.len = end - start
};
}
static inline bool is_whitespace(int ch) {
return ch == ' ' || ch == '\r' || ch == '\n'
|| ch == '\t' || ch == '\v' || ch == '\f';
}
str str_trim_left(str s) {
// TODO make more efficient!
while (s.len > 0 && is_whitespace(s.data[0])) {
s.data++;
s.len--;
}
return s;
}
str str_trim_right(str s) {
// TODO make more efficient!
while (s.len > 0 && is_whitespace(s.data[s.len - 1])) {
s.len--;
}
return s;
}
str str_trim(str s) {
return str_trim_right(str_trim_left(s));
}
bool str_eq(str a, str b) {
if (a.len != b.len) {
return false;
}
else {
for (size_t i = 0; i < a.len; ++i) {
if (a.data[i] != b.data[i]) {
return false;
}
}
return true;
}
}
bool str_starts_with(str s, str prefix) {
if (prefix.len > s.len) {
return false;
}
for (size_t index = 0; index < prefix.len; ++index) {
if (s.data[index] != prefix.data[index]) {
return false;
}
}
return true;
}
bool str_ends_with(str s, str suffix) {
if (suffix.len > s.len) {
return false;
}
size_t offset = s.len - suffix.len;
for (size_t index = 0; index < suffix.len; ++index) {
if (s.data[offset + index] != suffix.data[index]) {
return false;
}
}
return true;
}
bool str_empty(str s) {
return s.len == 0;
}
void str_print(str s) {
printf("%.*s", (int) s.len, s.data);
}
str_find_result str_find_left(str haystack, str needle) {
if (needle.len > haystack.len) {
return (str_find_result) { .found = false };
}
for (size_t start = 0; start < haystack.len - needle.len; ++start) {
bool found = true;
for (size_t offset = 0; offset < needle.len; ++offset) {
if (haystack.data[start + offset] != needle.data[offset]) {
found = false;
break;
}
}
if (found) {
return (str_find_result) { .found = true, .offset = start };
}
}
return (str_find_result) { .found = false };
}
str_find_result str_find_right(str haystack, str needle) {
if (needle.len > haystack.len) {
return (str_find_result) { .found = false };
}
// TODO is intmax_t a good idea?
for (intmax_t start = haystack.len - needle.len;
start >= 0;
++start)
{
bool found = true;
for (size_t offset = 0; offset < needle.len; ++offset) {
if (haystack.data[start + offset] != needle.data[offset]) {
found = false;
break;
}
}
if (found) {
return (str_find_result) { .found = true, .offset = start };
}
}
return (str_find_result) { .found = false };
}
str str_next_token(str *source, str delim) {
str_find_result match = str_find_left(*source, delim);
if (match.found) {
size_t skip_len = match.offset + delim.len;
str result = make_str(source->data, match.offset);
source->data += skip_len;
source->len -= skip_len;
return result;
}
else {
// if we reach here, we have reached the end of `source` without
// encountering the delimiter -> return the remainder of the string.
str result = *source;
source->data += source->len;
source->len = 0;
return result;
}
}
void str_split(str s, str delim, str *first, str *second) {
*first = str_next_token(&s, delim);
*second = s;
}
#endif /* STR_IMPLEMENTATION */
#endif /* STR_H */