diff options
| -rw-r--r-- | cstring/include/cstring.h | 140 |
1 files changed, 79 insertions, 61 deletions
diff --git a/cstring/include/cstring.h b/cstring/include/cstring.h index ae650eb..c8362ea 100644 --- a/cstring/include/cstring.h +++ b/cstring/include/cstring.h | |||
| @@ -7,67 +7,85 @@ | |||
| 7 | 7 | ||
| 8 | /// A fixed-size string. | 8 | /// A fixed-size string. |
| 9 | /// The string is null-terminated so that it can be used with the usual C APIs. | 9 | /// The string is null-terminated so that it can be used with the usual C APIs. |
| 10 | #define DEF_STRING(STRING, SIZE) \ | 10 | // |
| 11 | typedef struct STRING { \ | 11 | // TODO: The asserts on length should be hard asserts, not just asserts in debug |
| 12 | int length; \ | 12 | // builds. |
| 13 | char str[SIZE]; \ | 13 | #define DEF_STRING(STRING, SIZE) \ |
| 14 | } STRING; \ | 14 | typedef struct STRING { \ |
| 15 | \ | 15 | size_t length; \ |
| 16 | static const size_t STRING##_size = SIZE; \ | 16 | char str[SIZE]; \ |
| 17 | \ | 17 | } STRING; \ |
| 18 | static inline const char* STRING##_cstring(const STRING* str) { \ | 18 | \ |
| 19 | return str->str; \ | 19 | static const size_t STRING##_size = SIZE; \ |
| 20 | } \ | 20 | \ |
| 21 | \ | 21 | static inline const char* STRING##_cstring(const STRING* str) { \ |
| 22 | static inline STRING STRING##_make(const char* cstr) { \ | 22 | return str->str; \ |
| 23 | if (!cstr) { \ | 23 | } \ |
| 24 | return (STRING){0}; \ | 24 | \ |
| 25 | } else { \ | 25 | static inline size_t STRING##_length(STRING str) { return str.length; } \ |
| 26 | STRING str = (STRING){0}; \ | 26 | \ |
| 27 | str.length = strlcpy(str.str, cstr, SIZE); \ | 27 | static inline STRING STRING##_make(const char* cstr) { \ |
| 28 | return str; \ | 28 | if (!cstr) { \ |
| 29 | } \ | 29 | return (STRING){0}; \ |
| 30 | } \ | 30 | } else { \ |
| 31 | \ | 31 | STRING str = (STRING){0}; \ |
| 32 | static inline STRING STRING##_dirname(STRING path) { \ | 32 | str.length = strlcpy(str.str, cstr, SIZE); \ |
| 33 | STRING str = path; \ | 33 | return str; \ |
| 34 | for (int i = str.length - 1; i >= 0; --i) { \ | 34 | } \ |
| 35 | if (str.str[i] == '/' || str.str[i] == '\\') { \ | 35 | } \ |
| 36 | str.str[i] = 0; \ | 36 | \ |
| 37 | str.length = i; \ | 37 | static inline STRING STRING##_dirname(STRING path) { \ |
| 38 | return str; \ | 38 | STRING str = path; \ |
| 39 | } else { \ | 39 | for (int i = str.length - 1; i >= 0; --i) { \ |
| 40 | str.str[i] = 0; \ | 40 | if (str.str[i] == '/' || str.str[i] == '\\') { \ |
| 41 | } \ | 41 | str.str[i] = 0; \ |
| 42 | } \ | 42 | str.length = i; \ |
| 43 | str = (STRING){0}; \ | 43 | return str; \ |
| 44 | str.str[0] = '.'; \ | 44 | } else { \ |
| 45 | str.length = 1; \ | 45 | str.str[i] = 0; \ |
| 46 | return str; \ | 46 | } \ |
| 47 | } \ | 47 | } \ |
| 48 | \ | 48 | str = (STRING){0}; \ |
| 49 | static inline STRING STRING##_concat(STRING a, STRING b) { \ | 49 | str.str[0] = '.'; \ |
| 50 | assert(a.length + b.length + 1 < SIZE); \ | 50 | str.length = 1; \ |
| 51 | STRING str = {0}; \ | 51 | return str; \ |
| 52 | strlcpy(str.str, a.str, SIZE); \ | 52 | } \ |
| 53 | strlcpy(str.str + a.length, b.str, SIZE); \ | 53 | \ |
| 54 | str.length = a.length + b.length; \ | 54 | static inline void STRING##_append_cstr(STRING* a, const char* b) { \ |
| 55 | return str; \ | 55 | size_t b_length = strlen(b); \ |
| 56 | } \ | 56 | assert(a->length + b_length + 1 < SIZE); \ |
| 57 | \ | 57 | strlcpy(a->str + a->length, b, SIZE); \ |
| 58 | static inline STRING STRING##_concat_path(STRING a, STRING b) { \ | 58 | a->length = a->length + b_length; \ |
| 59 | return STRING##_concat(STRING##_concat(a, STRING##_make("/")), b); \ | 59 | } \ |
| 60 | } \ | 60 | \ |
| 61 | \ | 61 | static inline void STRING##_append(STRING* a, STRING b) { \ |
| 62 | static inline bool STRING##_eq(STRING a, STRING b) { \ | 62 | assert(a->length + b.length + 1 < SIZE); \ |
| 63 | if (a.length != b.length) { \ | 63 | strlcpy(a->str + a->length, b.str, SIZE); \ |
| 64 | return false; \ | 64 | a->length = a->length + b.length; \ |
| 65 | } \ | 65 | } \ |
| 66 | return strncmp(a.str, b.str, a.length) == 0; \ | 66 | \ |
| 67 | } \ | 67 | static inline STRING STRING##_concat(STRING a, STRING b) { \ |
| 68 | \ | 68 | assert(a.length + b.length + 1 < SIZE); \ |
| 69 | static inline bool STRING##_eq_cstr(STRING a, const char* b) { \ | 69 | STRING str = {0}; \ |
| 70 | return strncmp(a.str, b, a.length) == 0; \ | 70 | strlcpy(str.str, a.str, SIZE); \ |
| 71 | strlcpy(str.str + a.length, b.str, SIZE); \ | ||
| 72 | str.length = a.length + b.length; \ | ||
| 73 | return str; \ | ||
| 74 | } \ | ||
| 75 | \ | ||
| 76 | static inline STRING STRING##_concat_path(STRING a, STRING b) { \ | ||
| 77 | return STRING##_concat(STRING##_concat(a, STRING##_make("/")), b); \ | ||
| 78 | } \ | ||
| 79 | \ | ||
| 80 | static inline bool STRING##_eq(STRING a, STRING b) { \ | ||
| 81 | if (a.length != b.length) { \ | ||
| 82 | return false; \ | ||
| 83 | } \ | ||
| 84 | return strncmp(a.str, b.str, a.length) == 0; \ | ||
| 85 | } \ | ||
| 86 | \ | ||
| 87 | static inline bool STRING##_eq_cstr(STRING a, const char* b) { \ | ||
| 88 | return strncmp(a.str, b, a.length) == 0; \ | ||
| 71 | } | 89 | } |
| 72 | 90 | ||
| 73 | DEF_STRING(sstring, 32) // Small. | 91 | DEF_STRING(sstring, 32) // Small. |
