Add feature macros for strnlen
[str.git] / str.c
blob8eda09fecd1a00df1ffe1a0ef086991fef47a61f
1 /*
2 * str.c - string_t implementation.
3 * Author: Saúl Valdelvira (2023)
4 */
5 #define _POSIX_C_SOURCE 200809L
6 #include "str.h"
7 #include <stdlib.h>
8 #include <assert.h>
9 #include <string.h> // memcpy, strnlen
10 #include <stdarg.h>
11 #include "util.h"
13 #define INITIAL_SIZE 16
14 #ifndef GROW_FACTOR
15 #define GROW_FACTOR 2
16 #endif
17 static_assert(GROW_FACTOR > 1, "");
19 struct string {
20 char *buffer;
21 size_t length;
22 size_t buffer_size;
25 static void resize_buffer(string_t *str, size_t new_size){
26 if (new_size == 0)
27 new_size = 1;
28 str->buffer_size = new_size;
29 str->buffer = realloc(str->buffer, str->buffer_size * sizeof(char));
30 assert(str->buffer);
33 static INLINE
34 string_t* __str_init(unsigned int initial_size) {
35 string_t *str = malloc(sizeof(*str));
36 assert(str);
37 str->buffer = NULL;
38 resize_buffer(str, initial_size);
39 str->length = 0;
40 return str;
43 string_t* str_empty(void){
44 return __str_init(INITIAL_SIZE);
47 string_t* str_init(unsigned int initial_size){
48 return __str_init(initial_size);
51 string_t* str_from_cstr(const char *src, unsigned n){
52 if (!src)
53 return NULL;
54 size_t len = strnlen(src, n);
55 string_t *str = str_init(len);
56 str_concat_cstr(str, src, n);
57 return str;
60 void str_reserve(string_t *str, unsigned n){
61 if (str && str->buffer_size < n)
62 resize_buffer(str, n);
65 int str_concat_cstr(string_t *str, const char *cat, unsigned n){
66 if (!str || !cat)
67 return -1;
68 size_t len = strnlen(cat, n);
69 if (str->buffer_size - str->length < len){
70 size_t new_size = str->buffer_size * GROW_FACTOR;
71 if (new_size - str->length < len)
72 new_size += len;
73 resize_buffer(str, new_size);
75 memcpy(&str->buffer[str->length], cat, len * sizeof(char));
76 str->length += len;
77 return 1;
80 int str_concat_str(string_t *str, string_t *cat){
81 if (!str || !cat)
82 return -1;
83 return str_concat_cstr(str, cat->buffer, cat->length);
86 int str_push_char(string_t *str, char c){
87 return str_concat_cstr(str, (char[]){c,'\0'}, 2);
90 int str_pop(string_t *str){
91 if (!str)
92 return -1;
93 return str_remove_at(str, str->length - 1);
96 int str_remove_at(string_t *str, unsigned index){
97 if (!str)
98 return -1;
99 if (index >= str->length)
100 return -2;
101 if (index < str->length - 1)
102 memcpy(&str->buffer[index], &str->buffer[index + 1], (str->length - index - 1) * sizeof(char));
103 str->length--;
104 return 1;
107 int str_remove_range(string_t *str, unsigned start, unsigned end){
108 if (!str)
109 return -1;
110 if (end < start)
111 return -2;
112 if (end > str->length)
113 end = str->length;
114 size_t len = str->length - end;
115 memmove(&str->buffer[start], &str->buffer[end], len * sizeof(char));
116 str->length -= end - start;
117 return 1;
120 char str_get_at(string_t *str, unsigned index){
121 if (!str)
122 return -1;
123 else if (index >= str->length)
124 return -2;
125 return str->buffer[index];
128 int str_set_at(string_t *str, unsigned index, char c){
129 if (!str)
130 return -1;
131 else if (index >= str->length)
132 return -2;
133 return str->buffer[index] = c;
136 int str_insert_cstr(string_t *str, const char *insert, unsigned n, unsigned index){
137 if (!str || !insert)
138 return -1;
139 if (index > str->length)
140 return -2;
141 size_t len = strnlen(insert, n);
142 if (str->length + len > str->buffer_size){
143 size_t new_size = str->buffer_size * 2;
144 if (str->length + len > new_size)
145 new_size += len;
146 resize_buffer(str, new_size);
148 memmove(&str->buffer[index + len], &str->buffer[index], (str->length - index) * sizeof(char));
149 memcpy(&str->buffer[index], insert, len * sizeof(char));
150 str->length += len;
151 return 1;
154 int str_insert(string_t *str, char c, unsigned index){
155 return str_insert_cstr(str, (char[]){c, '\0'}, 2, index);
158 char* str_to_cstr(string_t *str){
159 if (!str)
160 return NULL;
161 char *cstr = malloc(str->length + 1);
162 memcpy(cstr, str->buffer, str->length * sizeof(char));
163 cstr[str->length] = '\0';
164 return cstr;
167 const char* str_get_buffer(string_t *str){
168 if (!str)
169 return NULL;
170 if (str->length == str->buffer_size)
171 resize_buffer(str, str->buffer_size * GROW_FACTOR);
172 str->buffer[str->length] = '\0';
173 return str->buffer;
176 char* str_substring(string_t *str, unsigned start, unsigned end){
177 if (!str || end < start)
178 return NULL;
179 if (end > str->length)
180 end = str->length;
181 size_t len = end - start;
182 char *substring = malloc((len + 1) * sizeof(char));
183 assert(substring);
184 memcpy(substring, &str->buffer[start], len * sizeof(char));
185 substring[len] = '\0';
186 return substring;
189 int str_transform(string_t *str, char(*func)(char)){
190 if (!str || !func)
191 return -1;
192 for (size_t i = 0; i < str->length; i++)
193 str->buffer[i] = func(str->buffer[i]);
194 return 1;
197 string_t* str_dup(string_t *str){
198 if (!str)
199 return NULL;
200 string_t *dup = str_init(str->length);
201 memcpy(dup->buffer, str->buffer, str->length * sizeof(char));
202 dup->length = str->length;
203 return dup;
206 size_t str_length(string_t *str){
207 if (!str)
208 return 0;
209 return str->length;
212 char* str_tok(string_t *str, char *tokens){
213 static char *prev_tok = NULL;
214 static size_t pos = 0;
215 static string_t *curr_str = NULL;
216 free(prev_tok);
217 prev_tok = NULL;
218 if (str != NULL){
219 pos = 0;
220 curr_str = str;
222 if (!curr_str || !tokens || pos == curr_str->length)
223 return NULL;
224 for (size_t i = pos; i < curr_str->length; i++){
225 for (char *t = tokens; *t != '\0'; t++){
226 if (curr_str->buffer[i] == *t){
227 size_t len = i - pos;
228 prev_tok = malloc((len + 1) * sizeof(char));
229 memcpy(prev_tok, &curr_str->buffer[pos], len * sizeof(char));
230 prev_tok[len] = '\0';
231 pos = i + 1;
232 return prev_tok;
236 size_t len = curr_str->length - pos;
237 prev_tok = malloc(len + 1);
238 memcpy(prev_tok, &curr_str->buffer[pos], len * sizeof(char));
239 prev_tok[len] = '\0';
240 pos = curr_str->length;
241 return prev_tok;
244 char** str_split(string_t *str, char *delim){
245 if (!str || !delim)
246 return NULL;
247 size_t delim_len = strlen(delim);
248 int count = 1;
249 int i = str_find_substring(str, delim, 0);
250 while (i >= 0){
251 count++;
252 i = str_find_substring(str, delim, i + delim_len);
254 count++; // For the NULL element at the end
255 char **split = malloc(count * sizeof(char*));
256 char **ptr = split;
257 int prev_i = 0;
258 i = str_find_substring(str, delim, 0);
259 while (i >= 0){
260 *ptr = str_substring(str, prev_i, i);
261 if (**ptr == '\0')
262 free(*ptr);
263 else
264 ptr++;
265 prev_i = i + delim_len;
266 i = str_find_substring(str, delim, prev_i);
268 if ((size_t)prev_i < str->length)
269 *ptr = str_substring(str, prev_i, str->length);
270 split[count - 1] = NULL;
271 return split;
274 int str_find_substring(string_t *str, const char *substr, unsigned start_at){
275 if (!str || !substr)
276 return -2;
277 if (start_at >= str->length)
278 return -3;
279 for (size_t i = start_at; i < str->length; i++){
280 const char *c = substr;
281 for (size_t j = i; j < str->length; j++){
282 if (*c != str->buffer[j])
283 break;
284 c++;
285 if (*c == L'\0')
286 return i;
289 return -1;
292 int str_replace(string_t *str, const char *substr, const char *replacement){
293 size_t substr_len = strlen(substr);
294 size_t replacement_len = strlen(replacement);
295 int n_replacements = 0;
296 int i = str_find_substring(str, substr, 0);
297 while (i >= 0){
298 n_replacements++;
299 str_remove_range(str, i, i + substr_len);
300 str_insert_cstr(str, replacement, replacement_len, i);
301 i = str_find_substring(str, substr, i + replacement_len);
303 return n_replacements;
306 void str_shrink(string_t *str){
307 if (str && str->buffer_size > str->length)
308 resize_buffer(str, str->length);
311 void str_clear(string_t *str){
312 if (str)
313 str->length = 0;
316 static INLINE void __str__free(string_t *str) {
317 if (str){
318 free(str->buffer);
319 free(str);
323 void (str_free)(string_t *str, ...){
324 if (!str)
325 return;
326 va_list arg;
327 va_start(arg, str);
328 do {
329 __str__free(str);
330 str = va_arg(arg, string_t*);
331 } while (str);
332 va_end(arg);
335 void str_free_all(unsigned int n, ...){
336 va_list arg;
337 va_start(arg, n);
338 while (n-- > 0){
339 string_t *str = va_arg(arg, string_t*);
340 str_free(str);
342 va_end(arg);