/// Fixed-size strings with value semantics. #pragma once #include #ifdef __linux__ #include #else #include #endif #include #include #include // ----------------------------------------------------------------------------- // Fix-sized strings. /// 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 { \ size_t length; \ char str[SIZE]; \ } STRING; \ \ static const size_t STRING##_size = SIZE; \ \ static inline const char* STRING##_cstr(const STRING* str) { \ return str->str; \ } \ \ static inline size_t STRING##_length(const 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(const 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_len( \ STRING* a, const char* b, const size_t b_length) { \ 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_cstr(STRING* a, const char* b) { \ STRING##_append_cstr_len(a, b, strlen(b)); \ } \ \ static inline void STRING##_append(STRING* a, const STRING b) { \ STRING##_append_cstr_len(a, b.str, b.length); \ } \ \ static inline STRING STRING##_concat_cstr_len( \ const STRING a, const char* b, const size_t b_length) { \ ASSERT(a.length + b_length + 1 <= SIZE); \ STRING str = {0}; \ strlcpy(str.str, a.str, SIZE); \ strlcpy(str.str + a.length, b, SIZE - a.length); \ str.length = a.length + b_length; \ return str; \ } \ \ static inline STRING STRING##_concat_cstr(const STRING a, const char* b) { \ return STRING##_concat_cstr_len(a, b, strlen(b)); \ } \ \ static inline STRING STRING##_concat(const STRING a, const STRING b) { \ return STRING##_concat_cstr_len(a, b.str, b.length); \ } \ \ static inline STRING STRING##_concat_path(const STRING a, const STRING b) { \ return STRING##_concat(STRING##_concat_cstr(a, "/"), b); \ } \ \ static inline bool STRING##_eq_cstr_len( \ const STRING a, const char* b, size_t b_length) { \ return (a.length == b_length) && strncmp(a.str, b, a.length) == 0; \ } \ \ static inline bool STRING##_eq_cstr(const STRING a, const char* b) { \ return STRING##_eq_cstr_len(a, b, strlen(b)); \ } \ \ static inline bool STRING##_eq(const STRING a, const STRING b) { \ return STRING##_eq_cstr_len(a, b.str, b.length); \ } \ \ static inline bool STRING##_empty(const STRING a) { return a.length == 0; } \ \ static inline STRING STRING##_itoa(int n) { \ STRING str = (STRING){0}; \ const int written = snprintf(str.str, SIZE, "%d", n); \ ASSERT(written >= 0); \ str.length = (size_t)written; \ return str; \ } \ \ static inline uint64_t STRING##_hash(const STRING str) { \ return cstring_hash(str.str); \ } /// Return a hash of the given string. static inline uint64_t cstring_hash(const char* str) { uint64_t hash = 0; for (size_t i = 0; i < strlen(str); ++i) { hash = (uint64_t)str[i] + (hash << 6) + (hash << 16) - hash; } return hash; } DEF_STRING(sstring, 32) // Small. DEF_STRING(mstring, 256) // Medium. DEF_STRING(lstring, 1024) // Large. DEF_STRING(xlstring, 4096) // Extra large. // ----------------------------------------------------------------------------- // Dynamically-sized strings. typedef struct string { const char* data; size_t length; } string; /// Create a new string. string string_new(const char*); /// Delete the string. void string_del(string*); /// Get the string's length. static inline size_t string_length(const string str) { return str.length; } /// Get the string's data. static inline const char* string_data(const string str) { return str.data; } /// Concatenate two strings. string string_concat(string left, string right); /// Convert a size to string. string string_from_size(size_t); /// Convert and format a size to string. /// The result uses B for bytes, K for kilobytes (1024), M for megabytes /// (2**20), and G for gigabytes (2**30), with two decimal digits. string string_format_size(size_t);