diff options
| -rw-r--r-- | cstring/include/cstring.h | 199 |
1 files changed, 105 insertions, 94 deletions
diff --git a/cstring/include/cstring.h b/cstring/include/cstring.h index 84976b1..b07dad6 100644 --- a/cstring/include/cstring.h +++ b/cstring/include/cstring.h | |||
| @@ -17,100 +17,111 @@ | |||
| 17 | 17 | ||
| 18 | /// A fixed-size string. | 18 | /// A fixed-size string. |
| 19 | /// The string is null-terminated so that it can be used with the usual C APIs. | 19 | /// The string is null-terminated so that it can be used with the usual C APIs. |
| 20 | #define DEF_STRING(STRING, SIZE) \ | 20 | #define DEF_STRING(STRING, SIZE) \ |
| 21 | typedef struct STRING { \ | 21 | typedef struct STRING { \ |
| 22 | size_t length; \ | 22 | size_t length; \ |
| 23 | char str[SIZE]; \ | 23 | char str[SIZE]; \ |
| 24 | } STRING; \ | 24 | } STRING; \ |
| 25 | \ | 25 | \ |
| 26 | static const size_t STRING##_size = SIZE; \ | 26 | static const size_t STRING##_size = SIZE; \ |
| 27 | \ | 27 | \ |
| 28 | static inline const char* STRING##_cstr(const STRING* str) { \ | 28 | static inline const char* STRING##_cstr(const STRING* str) { \ |
| 29 | return str->str; \ | 29 | return str->str; \ |
| 30 | } \ | 30 | } \ |
| 31 | \ | 31 | \ |
| 32 | static inline size_t STRING##_length(STRING str) { return str.length; } \ | 32 | static inline size_t STRING##_length(const STRING* str) { \ |
| 33 | \ | 33 | return str->length; \ |
| 34 | static inline STRING STRING##_make(const char* cstr) { \ | 34 | } \ |
| 35 | if (!cstr) { \ | 35 | \ |
| 36 | return (STRING){0}; \ | 36 | static inline STRING STRING##_make(const char* cstr) { \ |
| 37 | } else { \ | 37 | if (!cstr) { \ |
| 38 | STRING str = (STRING){0}; \ | 38 | return (STRING){0}; \ |
| 39 | str.length = strlcpy(str.str, cstr, SIZE); \ | 39 | } else { \ |
| 40 | return str; \ | 40 | STRING str = (STRING){0}; \ |
| 41 | } \ | 41 | str.length = strlcpy(str.str, cstr, SIZE); \ |
| 42 | } \ | 42 | return str; \ |
| 43 | \ | 43 | } \ |
| 44 | static inline STRING STRING##_dirname(STRING path) { \ | 44 | } \ |
| 45 | STRING str = path; \ | 45 | \ |
| 46 | for (int i = str.length - 1; i >= 0; --i) { \ | 46 | static inline STRING STRING##_dirname(const STRING path) { \ |
| 47 | if (str.str[i] == '/' || str.str[i] == '\\') { \ | 47 | STRING str = path; \ |
| 48 | str.str[i] = 0; \ | 48 | for (int i = str.length - 1; i >= 0; --i) { \ |
| 49 | str.length = i; \ | 49 | if (str.str[i] == '/' || str.str[i] == '\\') { \ |
| 50 | return str; \ | 50 | str.str[i] = 0; \ |
| 51 | } else { \ | 51 | str.length = i; \ |
| 52 | str.str[i] = 0; \ | 52 | return str; \ |
| 53 | } \ | 53 | } else { \ |
| 54 | } \ | 54 | str.str[i] = 0; \ |
| 55 | str = (STRING){0}; \ | 55 | } \ |
| 56 | str.str[0] = '.'; \ | 56 | } \ |
| 57 | str.length = 1; \ | 57 | str = (STRING){0}; \ |
| 58 | return str; \ | 58 | str.str[0] = '.'; \ |
| 59 | } \ | 59 | str.length = 1; \ |
| 60 | \ | 60 | return str; \ |
| 61 | static inline void STRING##_append_cstr(STRING* a, const char* b) { \ | 61 | } \ |
| 62 | size_t b_length = strlen(b); \ | 62 | \ |
| 63 | ASSERT(a->length + b_length + 1 < SIZE); \ | 63 | static inline void STRING##_append_cstr_len( \ |
| 64 | strlcpy(a->str + a->length, b, SIZE); \ | 64 | STRING* a, const char* b, const size_t b_length) { \ |
| 65 | a->length = a->length + b_length; \ | 65 | ASSERT(a->length + b_length + 1 <= SIZE); \ |
| 66 | } \ | 66 | strlcpy(a->str + a->length, b, SIZE - a->length); \ |
| 67 | \ | 67 | a->length += b_length; \ |
| 68 | static inline void STRING##_append(STRING* a, STRING b) { \ | 68 | } \ |
| 69 | ASSERT(a->length + b.length + 1 < SIZE); \ | 69 | \ |
| 70 | strlcpy(a->str + a->length, b.str, SIZE); \ | 70 | static inline void STRING##_append_cstr(STRING* a, const char* b) { \ |
| 71 | a->length = a->length + b.length; \ | 71 | STRING##_append_cstr_len(a, b, strlen(b)); \ |
| 72 | } \ | 72 | } \ |
| 73 | \ | 73 | \ |
| 74 | static inline STRING STRING##_concat(STRING a, STRING b) { \ | 74 | static inline void STRING##_append(STRING* a, const STRING b) { \ |
| 75 | ASSERT(a.length + b.length + 1 < SIZE); \ | 75 | STRING##_append_cstr_len(a, b.str, b.length); \ |
| 76 | STRING str = {0}; \ | 76 | } \ |
| 77 | strlcpy(str.str, a.str, SIZE); \ | 77 | \ |
| 78 | strlcpy(str.str + a.length, b.str, SIZE); \ | 78 | static inline STRING STRING##_concat_cstr_len( \ |
| 79 | str.length = a.length + b.length; \ | 79 | const STRING a, const char* b, const size_t b_length) { \ |
| 80 | return str; \ | 80 | ASSERT(a.length + b_length + 1 <= SIZE); \ |
| 81 | } \ | 81 | STRING str = {0}; \ |
| 82 | \ | 82 | strlcpy(str.str, a.str, SIZE); \ |
| 83 | static inline STRING STRING##_concat_cstr(STRING a, const char* b) { \ | 83 | strlcpy(str.str + a.length, b, SIZE - a.length); \ |
| 84 | return STRING##_concat(a, STRING##_make(b)); \ | 84 | str.length = a.length + b_length; \ |
| 85 | } \ | 85 | return str; \ |
| 86 | \ | 86 | } \ |
| 87 | static inline STRING STRING##_concat_path(STRING a, STRING b) { \ | 87 | \ |
| 88 | return STRING##_concat(STRING##_concat(a, STRING##_make("/")), b); \ | 88 | static inline STRING STRING##_concat_cstr(const STRING a, const char* b) { \ |
| 89 | } \ | 89 | return STRING##_concat_cstr_len(a, b, strlen(b)); \ |
| 90 | \ | 90 | } \ |
| 91 | static inline bool STRING##_eq(STRING a, STRING b) { \ | 91 | \ |
| 92 | if (a.length != b.length) { \ | 92 | static inline STRING STRING##_concat(const STRING a, const STRING b) { \ |
| 93 | return false; \ | 93 | return STRING##_concat_cstr_len(a, b.str, b.length); \ |
| 94 | } \ | 94 | } \ |
| 95 | return strncmp(a.str, b.str, a.length) == 0; \ | 95 | \ |
| 96 | } \ | 96 | static inline STRING STRING##_concat_path(const STRING a, const STRING b) { \ |
| 97 | \ | 97 | return STRING##_concat(STRING##_concat_cstr(a, "/"), b); \ |
| 98 | static inline bool STRING##_eq_cstr(STRING a, const char* b) { \ | 98 | } \ |
| 99 | return (a.length == strlen(b)) && strncmp(a.str, b, a.length) == 0; \ | 99 | \ |
| 100 | } \ | 100 | static inline bool STRING##_eq_cstr_len( \ |
| 101 | \ | 101 | const STRING a, const char* b, size_t b_length) { \ |
| 102 | static inline bool STRING##_empty(STRING a) { return a.length == 0; } \ | 102 | return (a.length == b_length) && strncmp(a.str, b, a.length) == 0; \ |
| 103 | \ | 103 | } \ |
| 104 | static inline STRING STRING##_itoa(int n) { \ | 104 | \ |
| 105 | STRING str = (STRING){0}; \ | 105 | static inline bool STRING##_eq_cstr(const STRING a, const char* b) { \ |
| 106 | const int written = snprintf(str.str, SIZE, "%d", n); \ | 106 | return STRING##_eq_cstr_len(a, b, strlen(b)); \ |
| 107 | ASSERT(written >= 0); \ | 107 | } \ |
| 108 | str.length = (size_t)written; \ | 108 | \ |
| 109 | return str; \ | 109 | static inline bool STRING##_eq(const STRING a, const STRING b) { \ |
| 110 | } \ | 110 | return STRING##_eq_cstr_len(a, b.str, b.length); \ |
| 111 | \ | 111 | } \ |
| 112 | static inline uint64_t STRING##_hash(STRING str) { \ | 112 | \ |
| 113 | return cstring_hash(str.str); \ | 113 | static inline bool STRING##_empty(const STRING a) { return a.length == 0; } \ |
| 114 | \ | ||
| 115 | static inline STRING STRING##_itoa(int n) { \ | ||
| 116 | STRING str = (STRING){0}; \ | ||
| 117 | const int written = snprintf(str.str, SIZE, "%d", n); \ | ||
| 118 | ASSERT(written >= 0); \ | ||
| 119 | str.length = (size_t)written; \ | ||
| 120 | return str; \ | ||
| 121 | } \ | ||
| 122 | \ | ||
| 123 | static inline uint64_t STRING##_hash(const STRING str) { \ | ||
| 124 | return cstring_hash(str.str); \ | ||
| 114 | } | 125 | } |
| 115 | 126 | ||
| 116 | /// Return a hash of the given string. | 127 | /// Return a hash of the given string. |
