Make the replace functions return the number of found matches
[str.git] / str.c
blob2d42253ba57da33eb668ef33df8bc81195713994
1 /*
2 * str.c - String implementation.
3 * Author: Saúl Valdelvira (2023)
4 */
5 #include "str.h"
6 #include <stdlib.h>
7 #include <assert.h>
8 #include <string.h> // memcpy, strnlen
9 #include <stdarg.h>
11 #define INITIAL_SIZE 16
12 #ifndef GROW_FACTOR
13 #define GROW_FACTOR 2
14 #endif
15 static_assert(GROW_FACTOR > 1);
17 struct String{
18 char *buffer;
19 size_t length;
20 size_t buffer_size;
23 static void resize_buffer(String *str, size_t new_size){
24 if (new_size == 0)
25 new_size = 1;
26 str->buffer_size = new_size;
27 str->buffer = realloc(str->buffer, str->buffer_size * sizeof(char));
28 assert(str->buffer);
31 String* str_empty(void){
32 return str_init(INITIAL_SIZE);
35 String* str_init(unsigned initial_size){
36 String *str = malloc(sizeof(*str));
37 assert(str);
38 str->buffer = NULL;
39 resize_buffer(str, initial_size);
40 str->length = 0;
41 return str;
44 String* str_from_cstr(const char *src, unsigned n){
45 if (!src)
46 return NULL;
47 size_t len = strnlen(src, n);
48 String *str = str_init(len);
49 str_concat_cstr(str, src, n);
50 return str;
53 void str_reserve(String *str, unsigned n){
54 if (str && str->buffer_size < n)
55 resize_buffer(str, n);
58 int str_concat_cstr(String *str, const char *cat, unsigned n){
59 if (!str || !cat)
60 return -1;
61 size_t len = strnlen(cat, n);
62 if (str->buffer_size - str->length < len){
63 size_t new_size = str->buffer_size * GROW_FACTOR;
64 if (new_size - str->length < len)
65 new_size += len;
66 resize_buffer(str, new_size);
68 memcpy(&str->buffer[str->length], cat, len * sizeof(char));
69 str->length += len;
70 return 1;
73 int str_concat_str(String *str, String *cat){
74 if (!str || !cat)
75 return -1;
76 return str_concat_cstr(str, cat->buffer, cat->length);
79 int str_push_char(String *str, char c){
80 return str_concat_cstr(str, (char[]){c,'\0'}, 2);
83 int str_pop(String *str){
84 if (!str)
85 return -1;
86 return str_remove_at(str, str->length - 1);
89 int str_remove_at(String *str, unsigned index){
90 if (!str)
91 return -1;
92 if (index >= str->length)
93 return -2;
94 if (index < str->length - 1)
95 memcpy(&str->buffer[index], &str->buffer[index + 1], (str->length - index - 1) * sizeof(char));
96 str->length--;
97 return 1;
100 int str_remove_range(String *str, unsigned start, unsigned end){
101 if (!str)
102 return -1;
103 if (end < start)
104 return -2;
105 if (end > str->length)
106 end = str->length;
107 size_t len = str->length - end;
108 memmove(&str->buffer[start], &str->buffer[end], len * sizeof(char));
109 str->length -= end - start;
110 return 1;
113 char str_get_at(String *str, unsigned index){
114 if (!str)
115 return -1;
116 else if (index >= str->length)
117 return -2;
118 return str->buffer[index];
121 int str_set_at(String *str, unsigned index, char c){
122 if (!str)
123 return -1;
124 else if (index >= str->length)
125 return -2;
126 return str->buffer[index] = c;
129 int str_insert_cstr(String *str, const char *insert, unsigned n, unsigned index){
130 if (!str || !insert)
131 return -1;
132 if (index > str->length)
133 return -2;
134 size_t len = strnlen(insert, n);
135 if (str->length + len > str->buffer_size){
136 size_t new_size = str->buffer_size * 2;
137 if (str->length + len > new_size)
138 new_size += len;
139 resize_buffer(str, new_size);
141 memmove(&str->buffer[index + len], &str->buffer[index], (str->length - index) * sizeof(char));
142 memcpy(&str->buffer[index], insert, len * sizeof(char));
143 str->length += len;
144 return 1;
147 int str_insert(String *str, char c, unsigned index){
148 return str_insert_cstr(str, (char[]){c, '\0'}, 2, index);
151 char* str_to_cstr(String *str){
152 if (!str)
153 return NULL;
154 char *cstr = malloc(str->length + 1);
155 memcpy(cstr, str->buffer, str->length * sizeof(char));
156 cstr[str->length] = '\0';
157 return cstr;
160 const char* str_get_buffer(String *str){
161 if (!str)
162 return NULL;
163 if (str->length == str->buffer_size)
164 resize_buffer(str, str->buffer_size * GROW_FACTOR);
165 str->buffer[str->length] = '\0';
166 return str->buffer;
169 char* str_substring(String *str, unsigned start, unsigned end){
170 if (!str || end < start)
171 return NULL;
172 if (end > str->length)
173 end = str->length;
174 size_t len = end - start;
175 char *substring = malloc((len + 1) * sizeof(char));
176 assert(substring);
177 memcpy(substring, &str->buffer[start], len * sizeof(char));
178 substring[len] = '\0';
179 return substring;
182 int str_transform(String *str, char(*func)(char)){
183 if (!str || !func)
184 return -1;
185 for (size_t i = 0; i < str->length; i++)
186 str->buffer[i] = func(str->buffer[i]);
187 return 1;
190 String* str_dup(String *str){
191 if (!str)
192 return NULL;
193 String *dup = str_init(str->length);
194 memcpy(dup->buffer, str->buffer, str->length * sizeof(char));
195 dup->length = str->length;
196 return dup;
199 size_t str_length(String *str){
200 if (!str)
201 return 0;
202 return str->length;
205 char* str_tok(String *str, char *tokens){
206 static char *prev_tok = NULL;
207 static size_t pos = 0;
208 static String *curr_str = NULL;
209 free(prev_tok);
210 prev_tok = NULL;
211 if (str != NULL){
212 pos = 0;
213 curr_str = str;
215 if (!curr_str || !tokens || pos == curr_str->length)
216 return NULL;
217 for (size_t i = pos; i < curr_str->length; i++){
218 for (char *t = tokens; *t != '\0'; t++){
219 if (curr_str->buffer[i] == *t){
220 size_t len = i - pos;
221 prev_tok = malloc((len + 1) * sizeof(char));
222 memcpy(prev_tok, &curr_str->buffer[pos], len * sizeof(char));
223 prev_tok[len] = '\0';
224 pos = i + 1;
225 return prev_tok;
229 size_t len = curr_str->length - pos;
230 prev_tok = malloc(len + 1);
231 memcpy(prev_tok, &curr_str->buffer[pos], len * sizeof(char));
232 prev_tok[len] = '\0';
233 pos = curr_str->length;
234 return prev_tok;
237 char** str_split(String *str, char *delim){
238 if (!str || !delim)
239 return NULL;
240 size_t delim_len = strlen(delim);
241 int count = 1;
242 int i = str_find_substring(str, delim, 0);
243 while (i >= 0){
244 count++;
245 i = str_find_substring(str, delim, i + delim_len);
247 count++; // For the NULL element at the end
248 char **split = malloc(count * sizeof(char*));
249 char **ptr = split;
250 int prev_i = 0;
251 i = str_find_substring(str, delim, 0);
252 while (i >= 0){
253 *ptr = str_substring(str, prev_i, i);
254 if (**ptr == '\0')
255 free(*ptr);
256 else
257 ptr++;
258 prev_i = i + delim_len;
259 i = str_find_substring(str, delim, prev_i);
261 if ((size_t)prev_i < str->length)
262 *ptr = str_substring(str, prev_i, str->length);
263 split[count - 1] = NULL;
264 return split;
267 int str_find_substring(String *str, const char *substr, unsigned start_at){
268 if (!str || !substr)
269 return -2;
270 if (start_at >= str->length)
271 return -3;
272 for (size_t i = start_at; i < str->length; i++){
273 const char *c = substr;
274 for (size_t j = i; j < str->length; j++){
275 if (*c != str->buffer[j])
276 break;
277 c++;
278 if (*c == L'\0')
279 return i;
282 return -1;
285 int str_replace(String *str, const char *substr, const char *replacement){
286 size_t substr_len = strlen(substr);
287 size_t replacement_len = strlen(replacement);
288 int n_replacements = 0;
289 int i = str_find_substring(str, substr, 0);
290 while (i >= 0){
291 n_replacements++;
292 str_remove_range(str, i, i + substr_len);
293 str_insert_cstr(str, replacement, replacement_len, i);
294 i = str_find_substring(str, substr, i + replacement_len);
296 return n_replacements;
299 void str_shrink(String *str){
300 if (str && str->buffer_size > str->length)
301 resize_buffer(str, str->length);
304 void str_clear(String *str){
305 if (str)
306 str->length = 0;
309 void str_free(String *str){
310 if (str){
311 free(str->buffer);
312 free(str);
316 void str_free_all(unsigned int n, ...){
317 va_list arg;
318 va_start(arg, n);
319 while (n-- > 0){
320 String *str = va_arg(arg, String*);
321 str_free(str);
323 va_end(arg);