2 * str.c - string_t implementation.
3 * Author: Saúl Valdelvira (2023)
5 #define _POSIX_C_SOURCE 200809L
9 #include <string.h> // memcpy, strnlen
13 #define INITIAL_SIZE 16
17 static_assert(GROW_FACTOR
> 1, "");
25 static void resize_buffer(string_t
*str
, size_t new_size
){
28 str
->buffer_size
= new_size
;
29 str
->buffer
= realloc(str
->buffer
, str
->buffer_size
* sizeof(char));
34 string_t
* __str_init(unsigned int initial_size
) {
35 string_t
*str
= malloc(sizeof(*str
));
38 resize_buffer(str
, initial_size
);
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
){
54 size_t len
= strnlen(src
, n
);
55 string_t
*str
= str_init(len
);
56 str_concat_cstr(str
, src
, n
);
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
){
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
)
73 resize_buffer(str
, new_size
);
75 memcpy(&str
->buffer
[str
->length
], cat
, len
* sizeof(char));
80 int str_concat_str(string_t
*str
, string_t
*cat
){
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
){
93 return str_remove_at(str
, str
->length
- 1);
96 int str_remove_at(string_t
*str
, unsigned index
){
99 if (index
>= str
->length
)
101 if (index
< str
->length
- 1)
102 memcpy(&str
->buffer
[index
], &str
->buffer
[index
+ 1], (str
->length
- index
- 1) * sizeof(char));
107 int str_remove_range(string_t
*str
, unsigned start
, unsigned end
){
112 if (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
;
120 char str_get_at(string_t
*str
, unsigned index
){
123 else if (index
>= str
->length
)
125 return str
->buffer
[index
];
128 int str_set_at(string_t
*str
, unsigned index
, char c
){
131 else if (index
>= str
->length
)
133 return str
->buffer
[index
] = c
;
136 int str_insert_cstr(string_t
*str
, const char *insert
, unsigned n
, unsigned index
){
139 if (index
> str
->length
)
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
)
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));
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
){
161 char *cstr
= malloc(str
->length
+ 1);
162 memcpy(cstr
, str
->buffer
, str
->length
* sizeof(char));
163 cstr
[str
->length
] = '\0';
167 const char* str_get_buffer(string_t
*str
){
170 if (str
->length
== str
->buffer_size
)
171 resize_buffer(str
, str
->buffer_size
* GROW_FACTOR
);
172 str
->buffer
[str
->length
] = '\0';
176 char* str_substring(string_t
*str
, unsigned start
, unsigned end
){
177 if (!str
|| end
< start
)
179 if (end
> str
->length
)
181 size_t len
= end
- start
;
182 char *substring
= malloc((len
+ 1) * sizeof(char));
184 memcpy(substring
, &str
->buffer
[start
], len
* sizeof(char));
185 substring
[len
] = '\0';
189 int str_transform(string_t
*str
, char(*func
)(char)){
192 for (size_t i
= 0; i
< str
->length
; i
++)
193 str
->buffer
[i
] = func(str
->buffer
[i
]);
197 string_t
* str_dup(string_t
*str
){
200 string_t
*dup
= str_init(str
->length
);
201 memcpy(dup
->buffer
, str
->buffer
, str
->length
* sizeof(char));
202 dup
->length
= str
->length
;
206 size_t str_length(string_t
*str
){
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
;
222 if (!curr_str
|| !tokens
|| pos
== curr_str
->length
)
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';
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
;
244 char** str_split(string_t
*str
, char *delim
){
247 size_t delim_len
= strlen(delim
);
249 int i
= str_find_substring(str
, delim
, 0);
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*));
258 i
= str_find_substring(str
, delim
, 0);
260 *ptr
= str_substring(str
, prev_i
, i
);
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
;
274 int str_find_substring(string_t
*str
, const char *substr
, unsigned start_at
){
277 if (start_at
>= str
->length
)
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
])
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);
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
){
316 static INLINE
void __str__free(string_t
*str
) {
323 void (str_free
)(string_t
*str
, ...){
330 str
= va_arg(arg
, string_t
*);
335 void str_free_all(unsigned int n
, ...){
339 string_t
*str
= va_arg(arg
, string_t
*);