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. |