/// Fixed-size strings with value semantics. #pragma once #include #include /// A fixed-size string. /// The string is null-terminated so that it can be used with the usual C APIs. #define DEF_STRING(STRING, SIZE) \ typedef struct STRING { \ int 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 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 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); \ } DEF_STRING(sstring, 32) // Small. DEF_STRING(mstring, 256) // Medium. DEF_STRING(lstring, 1024) // Large. DEF_STRING(xlstring, 4096) // Extra large.