/// Fixed-size strings with value semantics. #pragma once #include #include #include /// A fixed-size string. /// The string is null-terminated so that it can be used with the usual C APIs. // // TODO: The asserts on length should be hard asserts, not just asserts in debug // builds. #define DEF_STRING(STRING, SIZE) \ typedef struct STRING { \ size_t length; \ char str[SIZE]; \ } STRING; \ \ static const size_t STRING##_size = SIZE; \ \ static inline const char* STRING##_cstring(const STRING* str) { \ return str->str; \ } \ \ static inline size_t STRING##_length(STRING str) { return str.length; } \ \ static inline STRING STRING##_make(const char* cstr) { \ if (!cstr) { \ return (STRING){0}; \ } else { \ STRING str = (STRING){0}; \ str.length = strlcpy(str.str, cstr, SIZE); \ return str; \ } \ } \ \ static inline STRING STRING##_dirname(STRING path) { \ STRING str = path; \ for (int i = str.length - 1; i >= 0; --i) { \ if (str.str[i] == '/' || str.str[i] == '\\') { \ str.str[i] = 0; \ str.length = i; \ return str; \ } else { \ str.str[i] = 0; \ } \ } \ str = (STRING){0}; \ str.str[0] = '.'; \ str.length = 1; \ return str; \ } \ \ static inline void STRING##_append_cstr(STRING* a, const char* b) { \ size_t b_length = strlen(b); \ assert(a->length + b_length + 1 < SIZE); \ strlcpy(a->str + a->length, b, SIZE); \ a->length = a->length + b_length; \ } \ \ static inline void STRING##_append(STRING* a, STRING b) { \ assert(a->length + b.length + 1 < SIZE); \ strlcpy(a->str + a->length, b.str, SIZE); \ a->length = a->length + b.length; \ } \ \ static inline STRING STRING##_concat(STRING a, STRING b) { \ assert(a.length + b.length + 1 < SIZE); \ STRING str = {0}; \ strlcpy(str.str, a.str, SIZE); \ strlcpy(str.str + a.length, b.str, SIZE); \ str.length = a.length + b.length; \ return str; \ } \ \ static inline STRING STRING##_concat_path(STRING a, STRING b) { \ return STRING##_concat(STRING##_concat(a, STRING##_make("/")), b); \ } \ \ static inline bool STRING##_eq(STRING a, STRING b) { \ if (a.length != b.length) { \ return false; \ } \ return strncmp(a.str, b.str, a.length) == 0; \ } \ \ static inline bool STRING##_eq_cstr(STRING a, const char* b) { \ return strncmp(a.str, b, a.length) == 0; \ } DEF_STRING(sstring, 32) // Small. DEF_STRING(mstring, 256) // Medium. DEF_STRING(lstring, 1024) // Large. DEF_STRING(xlstring, 4096) // Extra large.