Make the replace functions return the number of found matches
[str.git] / wstr.c
blob176a7f65ff9c85a853c7b3d318533479463cd6cc
1 /*
2 * wstr.c - WString implementation.
3 * Author: Saúl Valdelvira (2023)
4 */
5 #include "wstr.h"
6 #include <stdlib.h>
7 #include <assert.h>
8 #include <string.h> // memcpy
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 WString{
18 wchar_t* buffer;
19 size_t length;
20 size_t buffer_size;
23 static void resize_buffer(WString *wstr, size_t new_size){
24 if (new_size == 0)
25 new_size = 1;
26 wstr->buffer_size = new_size;
27 wstr->buffer = realloc(wstr->buffer, wstr->buffer_size * sizeof(wchar_t));
28 assert(wstr->buffer);
31 WString* wstr_empty(void){
32 return wstr_init(INITIAL_SIZE);
35 WString* wstr_init(unsigned initial_size){
36 WString *wstr = malloc(sizeof(*wstr));
37 assert(wstr);
38 wstr->buffer = NULL;
39 resize_buffer(wstr, initial_size);
40 wstr->length = 0;
41 return wstr;
44 static size_t wstrnlen(const wchar_t *str, unsigned n){
45 size_t len = 0;
46 while (*str++ != L'\0' && n-- > 0)
47 len++;
48 return len;
51 WString* wstr_from_cwstr(const wchar_t *src, unsigned n){
52 if (!src)
53 return NULL;
54 size_t len = wstrnlen(src, n);
55 WString *wstr = wstr_init(len);
56 wstr_concat_cwstr(wstr, src, n);
57 return wstr;
60 void wstr_reserve(WString *wstr, unsigned n){
61 if (wstr && wstr->buffer_size < n)
62 resize_buffer(wstr, n);
65 static inline void resize_if_needed(WString *wstr, size_t size){
66 if (wstr->length + size > wstr->buffer_size){
67 size_t new_size = wstr->buffer_size * GROW_FACTOR;
68 if (wstr->length + size > new_size)
69 new_size += size;
70 resize_buffer(wstr, new_size);
74 int wstr_concat_cwstr(WString *wstr, const wchar_t *cat, unsigned n){
75 if (!wstr || !cat)
76 return -1;
77 size_t len = wstrnlen(cat, n);
78 resize_if_needed(wstr, len);
79 memcpy(&wstr->buffer[wstr->length], cat, len * sizeof(wchar_t));
80 wstr->length += len;
81 return 1;
84 int wstr_concat_cstr(WString *wstr, const char *cat, unsigned n){
85 if (!wstr || !cat)
86 return -1;
87 size_t len = strnlen(cat, n);
88 resize_if_needed(wstr, len);
89 wchar_t *start = &wstr->buffer[wstr->length];
90 while (--n > 0 && *cat)
91 *start++ = *cat++;
92 wstr->length += len;
93 return 1;
96 int wstr_concat_wstr(WString *wstr, WString *cat){
97 if (!wstr || !cat)
98 return -1;
99 return wstr_concat_cwstr(wstr, cat->buffer, cat->length);
102 int wstr_push_char(WString *wstr, wchar_t c){
103 return wstr_concat_cwstr(wstr, (wchar_t[]){c, L'\0'}, 2);
106 int wstr_pop(WString *wstr){
107 if (!wstr)
108 return -1;
109 return wstr_remove_at(wstr, wstr->length - 1);
112 int wstr_remove_at(WString *wstr, unsigned index){
113 if (!wstr)
114 return -1;
115 if (index >= wstr->length)
116 return -2;
117 if (index < wstr->length - 1)
118 memcpy(&wstr->buffer[index], &wstr->buffer[index + 1], (wstr->length - index - 1) * sizeof(wchar_t));
119 wstr->length--;
120 return 1;
123 int wstr_remove_range(WString *wstr, unsigned start, unsigned end){
124 if (!wstr)
125 return -1;
126 if (end < start)
127 return -2;
128 if (end > wstr->length)
129 end = wstr->length;
130 size_t len = wstr->length - end;
131 memmove(&wstr->buffer[start], &wstr->buffer[end], len * sizeof(wchar_t));
132 wstr->length -= end - start;
133 return 1;
136 wchar_t wstr_get_at(WString *wstr, unsigned index){
137 if (!wstr)
138 return -1;
139 else if (index >= wstr->length)
140 return -2;
141 return wstr->buffer[index];
144 int wstr_set_at(WString *wstr, unsigned index, wchar_t c){
145 if (!wstr)
146 return -1;
147 else if (index >= wstr->length)
148 return -2;
149 return wstr->buffer[index] = c;
152 int wstr_insert_cwstr(WString *wstr, const wchar_t *insert, unsigned n, unsigned index){
153 if (!wstr || !insert)
154 return -1;
155 if (index > wstr->length)
156 return -2;
157 size_t len = wstrnlen(insert, n);
158 resize_if_needed(wstr, len);
159 memmove(&wstr->buffer[index + len], &wstr->buffer[index], (wstr->length - index) * sizeof(wchar_t));
160 memcpy(&wstr->buffer[index], insert, len * sizeof(wchar_t));
161 wstr->length += len;
162 return 1;
165 int wstr_insert(WString *wstr, wchar_t c, unsigned index){
166 return wstr_insert_cwstr(wstr, (wchar_t[]){c, L'\0'}, 2, index);
169 wchar_t* wstr_to_cwstr(WString *wstr){
170 if (!wstr)
171 return NULL;
172 wchar_t *cwstr = malloc((wstr->length + 1) * sizeof(wchar_t));
173 memcpy(cwstr, wstr->buffer, wstr->length * sizeof(wchar_t));
174 cwstr[wstr->length] = '\0';
175 return cwstr;
178 const wchar_t* wstr_get_buffer(WString *wstr){
179 if (!wstr)
180 return NULL;
181 if (wstr->length == wstr->buffer_size)
182 resize_buffer(wstr, wstr->buffer_size * GROW_FACTOR);
183 wstr->buffer[wstr->length] = '\0';
184 return wstr->buffer;
187 wchar_t* wstr_substring(WString *wstr, unsigned start, unsigned end){
188 if (!wstr || end < start)
189 return NULL;
190 if (end > wstr->length)
191 end = wstr->length;
192 size_t len = end - start;
193 wchar_t *substring = malloc((len + 1) * sizeof(wchar_t));
194 assert(substring);
195 memcpy(substring, &wstr->buffer[start], len * sizeof(wchar_t));
196 substring[len] = '\0';
197 return substring;
200 WString* wstr_dup(WString *wstr){
201 if (!wstr)
202 return NULL;
203 WString *dup = wstr_init(wstr->length);
204 memcpy(dup->buffer, wstr->buffer, wstr->length * sizeof(wchar_t));
205 dup->length = wstr->length;
206 return dup;
209 size_t wstr_length(WString *wstr){
210 if (!wstr)
211 return 0;
212 return wstr->length;
215 wchar_t* wstr_tok(WString *wstr, wchar_t *tokens){
216 static wchar_t *prev_tok = NULL;
217 static size_t pos = 0;
218 static WString *curr_str = NULL;
219 free(prev_tok);
220 prev_tok = NULL;
221 if (wstr != NULL){
222 pos = 0;
223 curr_str = wstr;
225 if (!curr_str || !tokens || pos == curr_str->length)
226 return NULL;
227 for (size_t i = pos; i < curr_str->length; i++){
228 for (wchar_t *t = tokens; *t != '\0'; t++){
229 if (curr_str->buffer[i] == *t){
230 size_t len = i - pos;
231 prev_tok = malloc((len + 1) * sizeof(wchar_t));
232 memcpy(prev_tok, &curr_str->buffer[pos], len * sizeof(wchar_t));
233 prev_tok[len] = '\0';
234 pos = i + 1;
235 return prev_tok;
239 size_t len = curr_str->length - pos;
240 prev_tok = malloc((len + 1) * sizeof(wchar_t));
241 memcpy(prev_tok, &curr_str->buffer[pos], len * sizeof(wchar_t));
242 prev_tok[len] = L'\0';
243 pos = curr_str->length;
244 return prev_tok;
247 wchar_t** wstr_split(WString *wstr, wchar_t *delim){
248 if (!wstr || !delim)
249 return NULL;
250 size_t delim_len = wstrnlen(delim, -1);
251 int count = 1;
252 int i = wstr_find_substring(wstr, delim, 0);
253 while (i >= 0){
254 count++;
255 i = wstr_find_substring(wstr, delim, i + delim_len);
257 count++; // For the NULL element at the end
258 wchar_t **split = malloc(count * sizeof(wchar_t*));
259 wchar_t **ptr = split;
260 int prev_i = 0;
261 i = wstr_find_substring(wstr, delim, 0);
262 while (i >= 0){
263 *ptr = wstr_substring(wstr, prev_i, i);
264 if (**ptr == L'\0')
265 free(*ptr);
266 else
267 ptr++;
268 prev_i = i + delim_len;
269 i = wstr_find_substring(wstr, delim, prev_i);
271 if ((size_t)prev_i < wstr->length)
272 *ptr = wstr_substring(wstr, prev_i, wstr->length);
273 split[count - 1] = NULL;
274 return split;
277 int wstr_find_substring(WString *wstr, const wchar_t *substr, unsigned start_at){
278 if (!wstr || !substr)
279 return -2;
280 if (start_at >= wstr->length)
281 return -3;
282 for (size_t i = start_at; i < wstr->length; i++){
283 const wchar_t *c = substr;
284 for (size_t j = i; j < wstr->length; j++){
285 if (*c != wstr->buffer[j])
286 break;
287 c++;
288 if (*c == L'\0')
289 return i;
292 return -1;
295 int wstr_replace(WString *wstr, const wchar_t *substr, const wchar_t *replacement){
296 size_t substr_len = wstrnlen(substr, -1);
297 size_t replacement_len = wstrnlen(replacement, -1);
298 int n_replacements = 0;
299 int i = wstr_find_substring(wstr, substr, 0);
300 while (i >= 0){
301 n_replacements++;
302 wstr_remove_range(wstr, i, i + substr_len);
303 wstr_insert_cwstr(wstr, replacement, replacement_len, i);
304 i = wstr_find_substring(wstr, substr, i + replacement_len);
306 return n_replacements;
309 int wstr_transform(WString *wstr, wchar_t(*func)(wchar_t)){
310 if (!wstr || !func)
311 return -1;
312 for (size_t i = 0; i < wstr->length; i++)
313 wstr->buffer[i] = func(wstr->buffer[i]);
314 return 1;
317 void wstr_shrink(WString *wstr){
318 if (wstr && wstr->buffer_size > wstr->length)
319 resize_buffer(wstr, wstr->length);
322 void wstr_clear(WString *wstr){
323 if (wstr)
324 wstr->length = 0;
327 void wstr_free(WString *wstr){
328 if (wstr){
329 free(wstr->buffer);
330 free(wstr);
334 void wstr_free_all(unsigned int n, ...){
335 va_list arg;
336 va_start(arg, n);
337 while (n-- > 0){
338 WString *wstr = va_arg(arg, WString*);
339 wstr_free(wstr);
341 va_end(arg);