3 * https://github.com/carmesim/libstring
5 * Copyright (c) 2020 VinÃcius R. Miguel, Ivan dos Santos Muniz
6 * <vinicius.miguel at unifesp.br>
7 * <ivan.muniz at unifesp.br>
9 * Permission is hereby granted, free of charge, to any person obtaining a copy
10 * of this software and associated documentation files (the "Software"), to deal
11 * in the Software without restriction, including without limitation the rights
12 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13 * copies of the Software, and to permit persons to whom the Software is
14 * furnished to do so, subject to the following conditions:
16 * The above copyright notice and this permission notice shall be included in all
17 * copies or substantial portions of the Software.
19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
28 #include "libstring.h"
31 //! **** Defining internal functions **** !//
34 //! \brief __malloc Simple error-printing wrapper around malloc.
35 //! \param size Quantity of memory to be allocated
36 //! \return A pointer to the allocated memory.
38 void * __malloc(size_t size
)
40 void * ptr
= malloc(size
);
43 fprintf(stderr
, "Fatal error: malloc failed.\n");
50 //! \brief __strlen Portable and simple reimplementation of strlen
51 //! \param s The NUL-terminated char array whose length will be calculated.
52 //! \return The size of the given char array.
54 size_t __strlen(const char *s
)
57 for (i
= 0; s
[i
] != '\0'; i
++);
62 //! \brief __memeq Compares two char arrays and returns true if they're equal on their first `size` elements.
63 //! \param str1 First char array
64 //! \param str2 Second char array
65 //! \param size Number of elements to be compared
66 //! \return Returns true if both char arrays are equal on their first `size` elements.
67 //! In short, __memeq(s1, s2, n) <-> !memcmp(s1, s2, n)
68 bool __memeq(char * str1
, char * str2
, size_t size
)
72 char *pstr1
= str1
, *pstr2
= str2
;
76 if (*pstr1
++ != *pstr2
++)
86 //! \brief __strstr Verifies if `find` is a substring within `str`.
87 //! \param str A char array.
88 //! \param find The possible substring within `str`.
89 //! \return Returns NULL if `find` is not a substring of `str`.
90 //! This function does *not* verify if __strlen(find) > __strlen(str).
92 char * __strstr(char * str
, char *find
)
98 size_t len
=__strlen(find
);
101 for(sc
= *str
++; sc
!= c
; sc
= *str
++)
108 } while(!__memeq(str
, find
, len
));
119 //! Based on a version of strtok by Chris Dodd.
120 //! Update: this should be reentrant/safer now.
121 //! Note: this *will* modify `str`. Duplicate the string before usage.
122 char * __strtok(char *str
, char *delim
, char ** prev
)
128 char *end
= __strstr(str
, delim
);
131 *prev
= end
+ __strlen(delim
);
142 //! \brief __memcpy Simple implementation of memcpy. This implementation is specific to char arrays.
143 //! \param dest The destination char array, whose contents will be written to src.
144 //! \param src The source char array, where the contents of dest will be written to.
145 //! \param n The quantity of elements to be copied.
146 //! \return Returns the destination char array.
148 char * __memcpy(char * dest
, const char *src
, size_t n
) {
157 //! \brief __strcpy A simple string copy function. It will always null-terminate the destination char array.
158 //! \param dest Destination char array.
159 //! \param src Source char array.
160 //! \param size Quantitity of elements to be copied. It may be larger than __strlen(src), but the function will not write more than __strlen(src).
162 size_t __strcpy(char *dest
, const char *src
, size_t size
)
166 fprintf(stderr
, "In __strcpy: `size` should not be 0.\n");
170 size_t source_len
= __strlen(src
);
173 if (size
< source_len
) // Truncate to the size of the smaller string
181 __memcpy(dest
, src
, n
); // Copy the contents of src to dest
182 dest
[n
] = '\0'; // Null-terminate dest.
186 size_t __strcat(char *dest
, const char *src
, size_t size
)
188 size_t dest_len
= __strlen(dest
);
191 return(dest_len
+ __strcpy(dest
+ dest_len
, src
, size
- dest_len
));
193 return(dest_len
+ __strlen(src
));
197 * \struct alloc_node A single node of the allocation linked list.
198 * \param nbytes The number of bytes to be allocated.
199 * \property val A pointer to struct cstr.
200 * \property next A pointer to the next node in the allocation list.
205 struct alloc_node
* next
;
208 //! We keep a simple linked list of heap allocations as to allow for string_free_all()
209 struct alloc_node
* alloc_list_head
= NULL
;
212 //! \brief string_alloc Allocates memory for a cstr_t * and adds it to the allocation list.
213 //! \param nbytes The number of bytes to be allocated.
214 //! \return Returns a new cstr_t* allocated within the alloc. list.
216 cstr_t
* string_alloc (size_t nbytes
)
218 if (!alloc_list_head
) // If the list's head hasn't been initialized
220 alloc_list_head
= __malloc(sizeof(struct alloc_node
));
221 alloc_list_head
->val
= __malloc(sizeof(struct cstr
));
222 alloc_list_head
->val
->value
= __malloc(nbytes
+ 1);
223 alloc_list_head
->val
->size
= nbytes
-1; //! Remove one from nbytes because it includes the NULL-terminator.
224 alloc_list_head
->val
->reserved
= nbytes
;
225 alloc_list_head
->next
= NULL
;
226 return alloc_list_head
->val
;
229 struct alloc_node
* current
= alloc_list_head
;
230 while (current
->next
)
231 current
= current
->next
;
233 current
->next
= __malloc(sizeof(struct alloc_node
));
234 current
->next
->val
= __malloc(sizeof(struct cstr
));
235 current
->next
->val
->value
= __malloc(nbytes
+ 1);
236 current
->next
->val
->size
= nbytes
-1;
237 current
->next
->val
->reserved
= nbytes
;
238 current
->next
->next
= NULL
;
239 return current
->next
->val
;
243 //! \brief string_free_all Frees all heap memory allocated by libstring.
245 void string_free_all (void)
247 struct alloc_node
* current
= alloc_list_head
;
250 struct alloc_node
* temp
= current
;
251 current
= current
->next
;
252 free(temp
->val
->value
);
259 //! \brief string_to_lower_case Alters a string to contain only lower-case characters.
260 //! \param origin The string whose value will be converted to lower-case characters. This parameters does not get modified.
261 //! \return A brand new cstr_t * that contains the altered value.
263 cstr_t
* string_to_lower_case(cstr_t
* origin
)
266 lower
= string_init(origin
->value
);
267 __strcpy(lower
->value
, origin
->value
, origin
->size
);
270 for(i
=0; i
<lower
->size
; i
++)
272 if ((lower
->value
[i
] >= 'A') && (lower
->value
[i
] <= 'Z'))
274 lower
->value
[i
] |= ' ';
281 //! \brief string_to_upper_case Alters a string to contain only upper-case characters.
282 //! \param origin The string whose value will be converted to upper-case characters. This parameters does not get modified.
283 //! \return A brand new cstr_t * that contains the altered value.
284 //! Makes use of bit manipulation to alter the ASCII values.
286 cstr_t
* string_to_upper_case(cstr_t
* origin
)
289 upper
= string_init(origin
->value
);
290 __strcpy(upper
->value
, origin
->value
, origin
->size
);
293 for(i
=0; i
<upper
->size
; i
++)
295 if ((upper
->value
[i
] >= 'a') && (upper
->value
[i
] <= 'z'))
297 upper
->value
[i
] &= '_';
304 //! \brief string_init Initializes a new cstr_t *.
305 //! \param origin The char array to be the value of the new string.
306 //! \return A brand new cstr_t *.
308 cstr_t
* string_init(const char * origin
)
310 cstr_t
* new = string_alloc(__strlen(origin
)+1);
311 __strcpy(new->value
, origin
, __strlen(origin
));
316 //! \brief string_update Updates the value of an cstr_t *. Increases its memory reservation if needed.
317 //! \param str The cstr_t * to be modified.
318 //! \param new_val The new char array value.
319 //! \return The new string's size.
321 size_t string_update(cstr_t
* str
, const char * new_val
)
323 size_t new_string_len
= __strlen(new_val
);
324 if (new_string_len
> str
->reserved
)
326 if (!string_reserve(str
, new_string_len
+1))
328 fprintf(stderr
, "In string_replace: string_reserve(str, %zu) failed.", new_string_len
+1);
333 __strcpy(str
->value
, new_val
, new_string_len
);
334 //printf("strlen: %zu, new_string_len: %zu\n", __strlen(str->value), new_string_len);
335 str
->size
= new_string_len
;
336 return new_string_len
;
340 //! \brief string_contains
345 bool string_contains(cstr_t
* str1
, const char * str2
)
349 fprintf(stderr
, "In string_contains: `str1` unitialized.\n");
353 if (str1
->size
< __strlen(str2
))
355 //! str2 is bigger than str1, so it can't be a substring.
359 if(__strstr(str1
->value
, (char *) str2
))
361 return true; // If __strstr returned a non-NULL pointer, str2 is a substring of str1.
367 * \brief Initializes a new string, a poitner to cstr_t
368 * \param str The cstr_t * whose capacity will be altered.
369 * \param capacity The new memory reserve of the cstr_t *.
370 * \retval bool Returns true if the operation was succesful, false otherwise.
372 bool string_reserve(cstr_t
*str
, size_t capacity
)
376 fprintf(stderr
, "In string_reserve: string not initialized.\n");
380 if (capacity
< str
->size
)
382 //! TODO: Implement truncation?
383 fprintf(stderr
, "In string_reserve: new capacity supplied is smaller than the string's current size.\n");
387 char * val_backup
= __malloc(str
->size
+ 1);
388 str
->value
= realloc(str
->value
, capacity
);
391 //! TODO: needs testing
392 //! I couldn't get realloc to fail in order to test
393 fprintf(stderr
, "In string_reserve: reallocation failed with capacity %zu\n", capacity
);
394 str
->value
= val_backup
;
398 //! In this case, reallocation worked
399 free (val_backup
); //! Get rid of the backup
400 str
->reserved
= capacity
;
405 //! \brief string_concat_to Concatenates a string str2 to str1.
410 size_t string_concat_to(cstr_t
* str1
, const char * str2
)
414 fprintf(stderr
, "In string_concat_to: str1 is unitialized.\n");
417 size_t str2len
= __strlen(str2
);
422 if(str1
->reserved
< str1
->size
+ str2len
+ 1)
424 if(!string_reserve(str1
, str1
->size
+ str2len
+ 1))
426 fprintf(stderr
, "In string_concat_to: string_reserve failed.\n");
431 str1
->size
+= str2len
;
432 __strcat(str1
->value
, str2
, str1
->size
);
437 //! \brief string_concat Concatenates str2 to str1 and returns that value into a new cstr_t *.
440 //! \return A new value containing str1 + str2
442 cstr_t
* string_concat(cstr_t
* str1
, const char * str2
)
444 cstr_t
* new = string_init(str1
->value
);
446 size_t str2len
= __strlen(str2
);
447 if(new->reserved
< str1
->size
+ str2len
+ 1)
449 if(!string_reserve(new, new->size
+ str2len
+ 1))
451 fprintf(stderr
, "In string_concat: string_reserve failed.\n");
455 new->size
= str1
->size
+ str2len
;
456 __strcat(new->value
, str2
, new->size
);
461 //! \brief string_replace_char Replaces all instances of `before` in str to `after`.
462 //! \param str The cstr_t * to be altered.
463 //! \param before The char to be replaced by `after`.
464 //! \param after The char to replace `before`.
465 //! \return The quantity of changed characters.
467 //! TODO: should this function return a copy and maintain the original untouched?
469 size_t string_replace_char(cstr_t
*str
, char before
, char after
)
473 fprintf(stderr
, "In string_replace_char: str is uninitialized.\n");
477 size_t i
, modified
= 0;
479 for(i
=0; i
< str
->size
; i
++)
481 if(str
->value
[i
] == before
)
483 str
->value
[i
] = after
;
492 //! \brief string_swap Swaps the content of str1 with str2.
493 //! \param str1 An initialized cstr_t *.
494 //! \param str2 An initialized cstr_t *.
495 //! \return A success-run boolean.
497 bool string_swap(cstr_t
* str1
, cstr_t
* str2
)
501 fprintf(stderr
, "In string_swap: str1 uninitialized.\n");
506 fprintf(stderr
, "In string_swap: str2 unitialized.\n");
510 size_t str1_val_size
= str1
->size
;
511 char * str1_val_backup
= malloc(str1_val_size
+ 1);
514 fprintf(stderr
, "In string_swap: malloc failed.\n");
518 __strcpy(str1_val_backup
, str1
->value
, str1_val_size
);
519 size_t str1_res_backup
= str1
->reserved
;
521 str1
->value
= str2
->value
;
522 str1
->size
= str2
->size
;
523 str1
->reserved
= str2
->reserved
;
525 str2
->value
= str1_val_backup
;
526 str2
->size
= str1_val_size
;
527 str2
->reserved
= str1_res_backup
;