2 * wstr.c - WString implementation.
3 * Author: Saúl Valdelvira (2023)
8 #include <string.h> // memcpy
11 #define INITIAL_SIZE 16
15 static_assert(GROW_FACTOR
> 1);
23 static void resize_buffer(WString
*wstr
, size_t new_size
){
26 wstr
->buffer_size
= new_size
;
27 wstr
->buffer
= realloc(wstr
->buffer
, wstr
->buffer_size
* sizeof(wchar_t));
31 WString
* wstr_empty(void){
32 return wstr_init(INITIAL_SIZE
);
35 WString
* wstr_init(unsigned initial_size
){
36 WString
*wstr
= malloc(sizeof(*wstr
));
39 resize_buffer(wstr
, initial_size
);
44 static size_t wstrnlen(const wchar_t *str
, unsigned n
){
46 while (*str
++ != L
'\0' && n
-- > 0)
51 WString
* wstr_from_cwstr(const wchar_t *src
, unsigned n
){
54 size_t len
= wstrnlen(src
, n
);
55 WString
*wstr
= wstr_init(len
);
56 wstr_concat_cwstr(wstr
, src
, n
);
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
)
70 resize_buffer(wstr
, new_size
);
74 int wstr_concat_cwstr(WString
*wstr
, const wchar_t *cat
, unsigned n
){
77 size_t len
= wstrnlen(cat
, n
);
78 resize_if_needed(wstr
, len
);
79 memcpy(&wstr
->buffer
[wstr
->length
], cat
, len
* sizeof(wchar_t));
84 int wstr_concat_cstr(WString
*wstr
, const char *cat
, unsigned n
){
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
)
96 int wstr_concat_wstr(WString
*wstr
, WString
*cat
){
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
){
109 return wstr_remove_at(wstr
, wstr
->length
- 1);
112 int wstr_remove_at(WString
*wstr
, unsigned index
){
115 if (index
>= wstr
->length
)
117 if (index
< wstr
->length
- 1)
118 memcpy(&wstr
->buffer
[index
], &wstr
->buffer
[index
+ 1], (wstr
->length
- index
- 1) * sizeof(wchar_t));
123 int wstr_remove_range(WString
*wstr
, unsigned start
, unsigned end
){
128 if (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
;
136 wchar_t wstr_get_at(WString
*wstr
, unsigned index
){
139 else if (index
>= wstr
->length
)
141 return wstr
->buffer
[index
];
144 int wstr_set_at(WString
*wstr
, unsigned index
, wchar_t c
){
147 else if (index
>= wstr
->length
)
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
)
155 if (index
> wstr
->length
)
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));
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
){
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';
178 const wchar_t* wstr_get_buffer(WString
*wstr
){
181 if (wstr
->length
== wstr
->buffer_size
)
182 resize_buffer(wstr
, wstr
->buffer_size
* GROW_FACTOR
);
183 wstr
->buffer
[wstr
->length
] = '\0';
187 wchar_t* wstr_substring(WString
*wstr
, unsigned start
, unsigned end
){
188 if (!wstr
|| end
< start
)
190 if (end
> wstr
->length
)
192 size_t len
= end
- start
;
193 wchar_t *substring
= malloc((len
+ 1) * sizeof(wchar_t));
195 memcpy(substring
, &wstr
->buffer
[start
], len
* sizeof(wchar_t));
196 substring
[len
] = '\0';
200 WString
* wstr_dup(WString
*wstr
){
203 WString
*dup
= wstr_init(wstr
->length
);
204 memcpy(dup
->buffer
, wstr
->buffer
, wstr
->length
* sizeof(wchar_t));
205 dup
->length
= wstr
->length
;
209 size_t wstr_length(WString
*wstr
){
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
;
225 if (!curr_str
|| !tokens
|| pos
== curr_str
->length
)
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';
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
;
247 wchar_t** wstr_split(WString
*wstr
, wchar_t *delim
){
250 size_t delim_len
= wstrnlen(delim
, -1);
252 int i
= wstr_find_substring(wstr
, delim
, 0);
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
;
261 i
= wstr_find_substring(wstr
, delim
, 0);
263 *ptr
= wstr_substring(wstr
, prev_i
, i
);
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
;
277 int wstr_find_substring(WString
*wstr
, const wchar_t *substr
, unsigned start_at
){
278 if (!wstr
|| !substr
)
280 if (start_at
>= wstr
->length
)
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
])
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);
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)){
312 for (size_t i
= 0; i
< wstr
->length
; i
++)
313 wstr
->buffer
[i
] = func(wstr
->buffer
[i
]);
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
){
327 void wstr_free(WString
*wstr
){
334 void wstr_free_all(unsigned int n
, ...){
338 WString
*wstr
= va_arg(arg
, WString
*);