cstring

Lightweight string library for C
git clone git://git.christosmarg.xyz/cstring.git
Log | Files | Refs | README | LICENSE

commit e24fb7a48da9300b9c2d0e9f64312db037c7fd1d
parent 84348c13c16f391e9f9595d7c786e6de05ce8f08
Author: Christos Margiolis <christos@margiolis.net>
Date:   Mon, 12 Oct 2020 14:22:35 +0300

updated docs, still pending ersase fix, improved flag handling

Diffstat:
MREADME.md | 98++++++++++++++++++++++++++++++++++++++++++++++++++-----------------------------
Mcstring.3 | 81++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------------
Mcstring.c | 51+++++++++++++++++++++++++++++++--------------------
Mcstring.h | 51+++++++++++++++++++++++++++++++++++++++++----------
Mtests/test.c | 4++++
5 files changed, 202 insertions(+), 83 deletions(-)

diff --git a/README.md b/README.md @@ -21,7 +21,7 @@ $ cd /path/to/cstring $ sudo make uninstall ``` -In order to link `cstring` to your project use the `-lcstring` flag during the compilation. +In order to link `cstring` to your project use the `-lcstring` flag during compilation. ## Usage @@ -29,49 +29,75 @@ When using this library, you must to **always** call the `cstring_create` and `c functions whenever you want to make a new instance of `cstring` and stop using it respectively, in order not to cause any memory leaks, as there's no *constructor* and *destructor* to do it for you. -The recommended way of initializing an empty string is by doing `cstring foo = cstring_create("")`. +The recommended way of initializing an empty string is by doing `cstring foo = cstring_create(CSTRING_INIT_EMPTY)`. ## Functions -* `cstring_create`: Initiliaze string -* `cstring_delete`: Deallocate string -* `cstring_assign`: Assign new string -* `cstring_append`: Append to end of string -* `cstring_prepend`: Prepend to beginning of string -* `cstring_insert`: Insert to a specific index -* `cstring_erase`: Erase portion of string -* `cstring_erase_matching`: Erase first match -* `cstring_erase_all_matching`: Erase all matches -* `cstring_trim`: Trim character -* `cstring_push_back`: Add character to end of string -* `cstring_pop_back`: Remove the last character from string -* `cstring_replace_char`: Replace character at a specific index -* `cstring_replace_str`: Replace portion of string -* `cstring_substr`: Extract a substring -* `cstring_swap`: Swap contents of two strings -* `cstring_shrink_to_fit`: Shrink string's capacity to fit its size +* `cstring_create`: Initiliaze string. +* `cstring_delete`: Deallocate string. +* `cstring_assign`: Assign new string. +* `cstring_insert`: Insert at a specific index. +* `cstring_append`: Append to end of string. +* `cstring_prepend`: Prepend to beginning of string. +* `cstring_erase`: Erase portion of string. +* `cstring_erase_matching`: Erase first match. +* `cstring_erase_all_matching`: Erase all matches. +* `cstring_trim`: Trim character. +* `cstring_push_back`: Add character to end of string. +* `cstring_pop_back`: Remove the last character from string. +* `cstring_replace_char`: Replace character at a specific index. +* `cstring_replace_str`: Replace portion of string. +* `cstring_substr`: Extract a substring. +* `cstring_swap`: Swap contents of two strings. +* `cstring_sort`: Sort an array of `cstring` objects. +* `cstring_sort_partial`: Like `cstring_sort` but for part of the array. +* `cstring_sort_chars`: Sort `cstring`'s contents. +* `cstring_sort_chars_partial`: Like `cstring_sort_chars` but for part of the string. +* `cstring_shrink_to_fit`: Shrink string's capacity to fit its size. * `cstring_clear`: Clear contents of string -* `cstring_find`: Find index of first match (string) -* `cstring_find_first_of`: Find index of first match (character) -* `cstring_find_last_of`: Find index of last match (character) -* `cstring_front`: Get first character in string -* `cstring_back`: Get last character in string -* `cstring_empty`: Check if string is empty -* `cstring_starts_with_str`: Check if string starts with specified `char *` -* `cstring_ends_with_str`: Check if string ends with specified `char *` -* `cstring_starts_with_char`: Check if string starts with specified `char` -* `cstring_ends_with_char`: Check if string ends with specified `char` -* `cstring_copy`: Copy contents of a `char *` -* `cstring_resize`: Resize string -* `cstring_getline`: Read a line from a `FILE` stream -* `cstring_greater`: Check if `lhs` is greater than `rhs` -* `cstring_greater_or_equal`: True `lhs` is greater of equal to `rhs` -* `cstring_less`: Check if `lhs` is less than `rhs` -* `cstring_less_or_equal`: Check if `lhs` is less or equal to `rhs` +* `cstring_find`: Find first occurence of a pattern in string. +* `cstring_rfind`: Fnid last occurence of a pattern in string. +* `cstring_find_first_of`: Find first occurence of specified characters in string. +* `cstring_find_first_not_of`: Find the first character that does not match any of the specified characters. +* `cstring_find_last_of`: Find last occurence of specified characters in string. +* `cstring_find_last_not_of`: Find the last character that does not match any of the specified characters. +* `cstring_front`: Get first character in string. +* `cstring_back`: Get last character in string. +* `cstring_empty`: Check if string is empty. +* `cstring_starts_with_str`: Check if string starts with specified `char *`. +* `cstring_ends_with_str`: Check if string ends with specified `char *`. +* `cstring_starts_with_char`: Check if string starts with specified `char`. +* `cstring_ends_with_char`: Check if string ends with specified `char`. +* `cstring_data`: Get string's content in raw bytes. +* `cstring_copy`: Copy contents of a `char *`. +* `cstring_resize`: Resize string. +* `cstring_getline`: Read a line from a `FILE` stream. +* `cstring_equal`: Check if `lhs == rhs`. +* `cstring_greater`: Check if `lhs > rhs`. +* `cstring_greater_or_equal`: Check if `lhs >= rhs`. +* `cstring_less`: Check if `lhs < rhs`. +* `cstring_less_or_equal`: Check if `lhs <= rhs`. `lhs`: Left hand side `rhs`: Right hand side +## Macros + +* `CSTRING_OUT_OF_BOUNDS`: Check if `pos` is out of bounds. +* `CSTRING_ARR_LEN`: Determine an array's length. The macro must be called in the same function the array is declared. +* `CSTRING_FLAG_CHECK`: Check if flag is on. +* `CSTRING_MALLOC`: Allocate memory with error checking. +The following macros can only be used in debug mode +* `CSTIRNG_DBG_LOG`: Prints a message in the format of `DEBUG: file:line:func(): msg`. +* `CSTRING_DBG_LOG_CSTR_INFO`: Prints all the contents of a `cstring` struct. The argument *has* to be a pointer. +* `CSTRING_DBG_LOG_CSTR_INFO_NPTR`: Like `CSTRING_DBG_LOG_CSTR_INFO` but the argument has to be a non-pointer. +* `CSTRING_DBG_LOG_STR_INFO`: Print contents of a normal string. + +## Constants + +* `CSTRING_NPOS`: Signifies that a pattern hasn't been found inside the string. Its value is -1. +* `CSTRING_INIT_EMPTY`: Used with `cstring_create` in case the string is to be initiliazed as empty. + ## Example See `test.c` for more. diff --git a/cstring.3 b/cstring.3 @@ -25,20 +25,28 @@ option. .SH STRUCTURES AND ENUMS .TP .BR cstring -typedef struct cstring { +struct cstring { char *str; /* contents of string */ size_t len; /* string length */ size_t capacity; /* string capacity */ .br }; .TP -.BR cstring_flags -enum cstring_flags { - CSTRING_ASCENDING, /* sort in ascending order */ - CSTRING_DESCENDING, /* sort in descending order */ - CSTRING_CALLBACK /* use your own sort function */ +.BR cstring_sort_flags +enum cstring_sort_flags { + CSTRING_SORT_ASCENDING = 1 << 0, /* sort in ascending order */ + CSTRING_SORT_DESCENDING = 1 << 1, /* sort in descending order */ + CSTRING_SORT_CALLBACK = 1 << 2, /* use your own sort function */ + CSTRING_SORT_REST = 1 << 3 /* sort the rest of the array */ .br }; +.SH TYPEDEFS +.TP +.BR typedef\ struct\ cstring\ cstring; +Short typedef for the cstring structure. +.TP +.BR typedef\ int\ (*cstring_sort_callback)(const\ void\ *,\ const\ void\ *); +Used in sort functions. .SH FUNCTIONS .TP .BR cstring\ cstring_create(const\ char\ *s) @@ -91,20 +99,30 @@ Extract a substring from current string. .BR void\ cstring_swap(cstring\ *lhs,\ cstring\ *rhs) Swap contents of two strings. .TP -.BR void\ cstring_sort(cstring\ **cs,\ size_t\ len,\ enum\ cstring_flags\ flags,\ int\ (*callback)(const\ void\ *lhs,\ const\ void\ *rhs)) +.BR void\ cstring_sort(cstring\ **cs,\ size_t\ len,\ enum\ cstring_sort_flags\ flags,\ cstring_sort_callback\ callback) Sort an array of cstrings. If you want to use the builtin comparison pass .I NULL in the last argument. In case you want to use your own callback use the -.I CSTRING_CALLBACK +.I CSTRING_SORT_CALLBACK flag and pass your own callback function in the last argument. .TP -.BR void\ cstring_sort_chars(cstring\ *cs,\ enum\ cstring_flags\ flags,\ int\ (*callback)(const\ void\ *lhs,\ const\ void\ *rhs)) +.BR void\ cstring_sort_partial(cstring\ **cs,\ size_t\ pos,\ size_t\ len,\ enum\ cstring_sort_flags\ flags,\ cstring_sort_callback\ callback) +Like +.B cstring_sort +but for specified part of an array. +.TP +.BR void\ cstring_sort_chars(cstring\ *cs,\ enum\ cstring_sort_flags\ flags,\ cstring_sort_callback\ callback) Sort a cstring's contents. If you want to use the builtin comparison pass .I NULL in the last argument. In case you want to use your own callback use the -.I CSTRING_CALLBACK +.I CSTRING_SORT_CALLBACK flag and pass your own callback function in the last argument. .TP +.BR void\ cstring_sort_chars_partial(cstring\ *cs,\ size_t\ pos,\ size_t\ len,\ enum\ cstring_sort_flags\ flags,\ cstring_sort_callback\ callback) +Like +.B cstring_sort_chars +but for specified part of string. +.TP .BR void\ cstring_shrink_to_fit(cstring\ *cs) Reduce string's capacity to its size. .TP @@ -155,7 +173,7 @@ Check to see if string ends with .I c .TP .BR void\ *cstring_data(const\ cstring\ *cs) -Get raw bytes of string's content. +Get string's content in raw bytes. .TP .BR char\ *cstring_copy(const\ char\ *s) Make a copy of a given @@ -173,6 +191,21 @@ Read a line from a .I FILE stream. Similar behavior to .I stdio's\ getline +.TP +.BR int\ cstring_equal(const\ cstring\ *lhs,\ const\ cstring\ *rhs) +Check if lhs == rhs +.TP +.BR int\ cstring_greater(const\ cstring\ *lhs,\ const\ cstring\ *rhs) +Check if lhs > rhs +.TP +.BR int\ cstring_greater_or_equal(const\ cstring\ *lhs,\ const\ cstring\ *rhs) +Check if lhs >= rhs +.TP +.BR int\ cstring_less(const\ cstring\ *lhs,\ const\ cstring\ *rhs) +Check if lhs < rhs +.TP +.BR int\ cstring_less_or_equal(const\ cstring\ *lhs,\ const\ cstring\ *rhs) +Check if lhs <= rhs .SH MACROS .TP .BR CSTRING_OUT_OF_BOUNDS(cs,\ pos) @@ -180,6 +213,15 @@ Check if .I pos is out of bounds. .TP +.BR CSTRING_ARR_LEN(arr) +Determine an array's length. The macro must be called in the same function +the array is declared. +.TP +.BR CSTRING_FLAG_CHECK(flag,\ bit) +Check if a flag is on. This macro is used for checking +.B cstring_sort_flags +in the implementation, but it can be used everywhere. +.TP .BR CSTRING_MALLOC(ptr,\ size) Allocate memory with error cheking. .P @@ -194,17 +236,22 @@ Print all the contents of a struct. The argument has to be a pointer. .TP .BR CSTRING_DBG_LOG_CSTR_INFO_NPTR(cs) -Print all the contents of a -.I cstring -struct. The argument has to be a non-pointer. +Like +.B CSTRING_DBG_LOG_CSTR_INFO +but the argument has to be a non-pointer. .TP -.BR CSTRING_DBG_LOG_STR_INFO(s, len) +.BR CSTRING_DBG_LOG_STR_INFO(s,\ len) Print contents of a normal string. .SH CONSTANTS .TP .BR CSTRING_NPOS This constant signifies that a pattern hasn't been found inside -the string. It's value is -1. +the string. Its value is -1. +.TP +.BR CSTRING_INIT_EMPTY +Used with +.B cstring_create +in case the string is to be initliazed as empty. .SH USAGE You must .B always @@ -218,7 +265,7 @@ and stop using it respectively, in order to not cause any memory leaks. .P The recommended way of initializing an empty string is by doing -.I cstring foo = cstring_create("") +.I cstring foo = cstring_create(CSTRING_INIT_EMPTY) .P If a function requires a .I char * diff --git a/cstring.c b/cstring.c @@ -257,30 +257,41 @@ cstring_swap(cstring *lhs, cstring *rhs) } void -cstring_sort(cstring **cs, - size_t len, - enum cstring_flags flags, - int (*callback)(const void *lhs, const void *rhs)) +cstring_sort_partial(cstring **cs, + size_t pos, + size_t len, + enum cstring_sort_flags flags, + cstring_sort_callback callback) { - if (flags == CSTRING_ASCENDING) - qsort(cs, len, sizeof(cstring *), cstring_cmp_greater); - else if (flags == CSTRING_DESCENDING) - qsort(cs, len, sizeof(cstring *), cstring_cmp_less); - else if (flags == CSTRING_CALLBACK) - qsort(cs, len, sizeof(cstring *), callback); + if (CSTRING_FLAG_CHECK(flags, CSTRING_SORT_REST) + || pos + len > len) /* maybe chanage out of bounds macro */ + len -= pos; + + if (CSTRING_FLAG_CHECK(flags, CSTRING_SORT_ASCENDING)) + qsort(cs + pos, len, sizeof(cstring *), cstring_cmp_greater); + else if (CSTRING_FLAG_CHECK(flags, CSTRING_SORT_DESCENDING)) + qsort(cs + pos, len, sizeof(cstring *), cstring_cmp_less); + else if (CSTRING_FLAG_CHECK(flags, CSTRING_SORT_CALLBACK)) + qsort(cs + pos, len, sizeof(cstring *), callback); } void -cstring_sort_chars(cstring *cs, - enum cstring_flags flags, - int (*callback)(const void *lhs, const void *rhs)) +cstring_sort_chars_partial(cstring *cs, + size_t pos, + size_t len, + enum cstring_sort_flags flags, + cstring_sort_callback callback) { - if (flags == CSTRING_ASCENDING) - qsort(cs->str, cs->len, sizeof(char), cstring_cmp_char_greater); - else if (flags == CSTRING_DESCENDING) - qsort(cs->str, cs->len, sizeof(char), cstring_cmp_char_less); - else if (flags == CSTRING_CALLBACK) - qsort(cs->str, cs->len, sizeof(char), callback); + if (CSTRING_FLAG_CHECK(flags, CSTRING_SORT_REST) + || CSTRING_OUT_OF_BOUNDS(cs, pos + len)) + len = cs->len - pos; + + if (CSTRING_FLAG_CHECK(flags, CSTRING_SORT_ASCENDING)) + qsort(cs->str + pos, len, sizeof(char), cstring_cmp_char_greater); + else if (CSTRING_FLAG_CHECK(flags, CSTRING_SORT_DESCENDING)) + qsort(cs->str + pos, len, sizeof(char), cstring_cmp_char_less); + else if (CSTRING_FLAG_CHECK(flags, CSTRING_SORT_CALLBACK)) + qsort(cs->str + pos, len, sizeof(char), callback); } void @@ -309,7 +320,7 @@ size_t cstring_rfind(const cstring *cs, const char *s) { CSTRING_CHECK(cs, s); - /* SIMPLIFY */ + /*SIMPLIFY */ int found; size_t idx = -1; size_t slen = strlen(s); diff --git a/cstring.h b/cstring.h @@ -10,7 +10,10 @@ extern "C" { #endif /* __cplusplus */ #define CSTRING_NPOS -1 +#define CSTRING_INIT_EMPTY "" #define CSTRING_OUT_OF_BOUNDS(cs, pos) ((pos) > cs->len) +#define CSTRING_ARR_LEN(arr) ((size_t)sizeof((arr)) / sizeof((arr)[0])) +#define CSTRING_FLAG_CHECK(flag, bit) (((flag) & (int)(bit)) == (int)(bit)) #define CSTRING_MALLOC(ptr, size) do { \ ptr = (char *)malloc((size)); \ @@ -35,18 +38,22 @@ extern "C" { CSTRING_DBG_LOG("S: %s | LEN: %ld\n", (s), (len)) #endif /* CSTRING_DBG */ -typedef struct cstring { +struct cstring { char *str; size_t len; size_t capacity; -} cstring; +}; -enum cstring_flags { - CSTRING_ASCENDING, - CSTRING_DESCENDING, - CSTRING_CALLBACK +enum cstring_sort_flags { + CSTRING_SORT_ASCENDING = 1 << 0, + CSTRING_SORT_DESCENDING = 1 << 1, + CSTRING_SORT_CALLBACK = 1 << 2, + CSTRING_SORT_REST = 1 << 3 }; +typedef struct cstring cstring; +typedef int (*cstring_sort_callback)(const void *, const void *); + extern cstring cstring_create(const char *); extern void cstring_delete(cstring *); extern void cstring_assign(cstring *, const char *); @@ -61,10 +68,12 @@ extern void cstring_replace_char(cstring *, size_t, char); extern void cstring_replace_str(cstring *, const char *, size_t, size_t); extern cstring cstring_substr(const cstring *, size_t, size_t); extern void cstring_swap(cstring *, cstring *); -extern void cstring_sort(cstring **, size_t, enum cstring_flags, - int (*)(const void *, const void *)); -extern void cstring_sort_chars(cstring *cs, enum cstring_flags, - int (*)(const void *, const void *)); +extern void cstring_sort_partial(cstring **, size_t, size_t, + enum cstring_sort_flags, + cstring_sort_callback); +extern void cstring_sort_chars_partial(cstring *cs, size_t, size_t, + enum cstring_sort_flags, + cstring_sort_callback); extern void cstring_clear(cstring *); extern size_t cstring_find(const cstring *, const char *); extern size_t cstring_rfind(const cstring *, const char *); @@ -76,9 +85,14 @@ extern char *cstring_copy(const char *); extern void cstring_resize(cstring *, size_t); extern cstring *cstring_getline(FILE *, cstring *, char); + /* static inlines */ static inline void cstring_prepend(cstring *, const char *); static inline void cstring_append(cstring *, const char *); +static inline void cstring_sort(cstring **, size_t, enum cstring_sort_flags, + cstring_sort_callback); +static inline void cstring_sort_chars(cstring *, enum cstring_sort_flags, + cstring_sort_callback); static inline void cstring_shrink_to_fit(cstring *); static inline int cstring_empty(const cstring *); static inline char cstring_front(const cstring *); @@ -107,6 +121,23 @@ cstring_append(cstring *cs, const char *s) } static inline void +cstring_sort(cstring **cs, + size_t len, + enum cstring_sort_flags flags, + cstring_sort_callback callback) +{ + cstring_sort_partial(cs, 0, len, flags, callback); +} + +static inline void +cstring_sort_chars(cstring *cs, + enum cstring_sort_flags flags, + cstring_sort_callback callback) +{ + cstring_sort_chars_partial(cs, 0, cs->len, flags, callback); +} + +static inline void cstring_shrink_to_fit(cstring *cs) { cstring_resize(cs, cs->len); diff --git a/tests/test.c b/tests/test.c @@ -2,12 +2,16 @@ // Compilation: gcc test.c -lcstring + int main(int argc, char **argv) { cstring s = cstring_create("Hello world"); printf("cstring_create: %s (Len: %ld, Capacity: %ld)\n", s.str, s.len, s.capacity); + cstring_sort_chars(&s, CSTRING_SORT_ASCENDING, NULL); + printf("cstring_sort_chars: %s (Len: %ld, Capacity: %ld)\n", s.str, s.len, s.capacity); + // BUG cstring_assign(&s, "New string"); printf("cstring_assign: %s (Len: %ld, Capacity: %ld)\n", s.str, s.len, s.capacity);