Skip to content

Commit

Permalink
str_sort() function added.
Browse files Browse the repository at this point in the history
  • Loading branch information
maxim2266 committed Apr 12, 2020
1 parent 3a8c30e commit d8eff0e
Show file tree
Hide file tree
Showing 4 changed files with 109 additions and 18 deletions.
13 changes: 11 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,9 @@ function resets its source object to an empty string.
### Safety
The `str` structure should be treated as opaque (i.e., do not attempt to directly access
or modify the fields in this structure).
As already mentioned, C language offers no help in preventing mistakes like direct assignment
to an owning object, but at least some types of mistakes can be eliminated via careful API design.
For example, in this library all functions producing a new owning string do not simply
Expand Down Expand Up @@ -151,10 +154,10 @@ Lexicographically compares the two string objects, with usual semantics.
`bool str_eq(const str s1, const str s2)`<br>
Returns "true" if the two strings match exactly.

`int str_case_cmp(const str s1, const str s2)`<br>
`int str_cmp_ci(const str s1, const str s2)`<br>
Case-insensitive comparison of two strings, implemented using `strncasecmp(3)`.

`bool str_case_eq(const str s1, const str s2`<br>
`bool str_eq_ci(const str s1, const str s2`<br>
Returns "true" is the two strings match case-insensitively.

`void str_cat_range(str* const dest, const str* const src, const size_t n)`<br>
Expand Down Expand Up @@ -198,6 +201,12 @@ Creates an owning object for the specified range of bytes. The range should be s
Creates an owning object from the given C string. The string should be safe to pass to
`free(3)` function. Destination is assigned using `str_assign` semantics.

#### sorting

`void str_sort(const str_cmp_func cmp, str* const array, const size_t count)`<br>
Sorts the given array of `str` objects. A number of typically used sorting functions is
provided (see `str.h` file).

#### Memory allocation
By default the library uses `malloc(3)` for memory allocations, and calls `abort(3)`
if the allocation fails. This behaviour can be changed by hash-defining `STR_EXT_ALLOC`
Expand Down
30 changes: 29 additions & 1 deletion str.c
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ int str_cmp(const str s1, const str s2)
}

// case-insensitive comparison
int str_case_cmp(const str s1, const str s2)
int str_cmp_ci(const str s1, const str s2)
{
const size_t n1 = str_len(s1), n2 = str_len(s2);

Expand Down Expand Up @@ -298,4 +298,32 @@ void str_join_range_ignore_empty(str* const dest, const str sep, const str* cons
// null-terminate and acquire
*p = 0;
str_acquire_chars(dest, buff, num_bytes);
}

// sorting: comparison functions
int str_order_asc(const void* const s1, const void* const s2)
{
return str_cmp(*(const str*)s1, *(const str*)s2);
}

int str_order_desc(const void* const s1, const void* const s2)
{
return -str_cmp(*(const str*)s1, *(const str*)s2);
}

int str_order_asc_ci(const void* const s1, const void* const s2)
{
return str_cmp_ci(*(const str*)s1, *(const str*)s2);
}

int str_order_desc_ci(const void* const s1, const void* const s2)
{
return -str_cmp_ci(*(const str*)s1, *(const str*)s2);
}

// sorting
void str_sort(const str_cmp_func cmp, str* const array, const size_t count)
{
if(array && cmp && count > 1)
qsort(array, count, sizeof(array[0]), cmp);
}
16 changes: 14 additions & 2 deletions str.h
Original file line number Diff line number Diff line change
Expand Up @@ -104,11 +104,11 @@ static inline
bool str_eq(const str s1, const str s2) { return str_cmp(s1, s2) == 0; }

// case-insensitive comparison
int str_case_cmp(const str s1, const str s2);
int str_cmp_ci(const str s1, const str s2);

// case-insensitive match
static inline
bool str_case_eq(const str s1, const str s2) { return str_case_cmp(s1, s2) == 0; }
bool str_eq_ci(const str s1, const str s2) { return str_cmp_ci(s1, s2) == 0; }

// concatenate strings
void str_cat_range(str* const dest, const str* const src, const size_t n);
Expand Down Expand Up @@ -167,6 +167,18 @@ void str_acquire_chars(str* const dest, const char* const s, size_t n);
// take ownership of the given string; totally unsafe, use at your own risk
void str_acquire(str* const dest, const char* const s);

// sorting
// comparison functions
typedef int (*str_cmp_func)(const void*, const void*);

int str_order_asc(const void* const s1, const void* const s2);
int str_order_desc(const void* const s1, const void* const s2);
int str_order_asc_ci(const void* const s1, const void* const s2);
int str_order_desc_ci(const void* const s1, const void* const s2);

// sort array of strings
void str_sort(const str_cmp_func cmp, str* const array, const size_t count);

#ifdef __cplusplus
}
#endif
68 changes: 55 additions & 13 deletions str_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -116,21 +116,21 @@ void test_str_cmp(void)
}

static
void test_str_case_cmp(void)
void test_str_cmp_ci(void)
{
const str s = str_lit("zzz");

assert(str_case_cmp(s, s) == 0);
assert(str_case_cmp(s, str_lit("zzz")) == 0);
assert(str_case_cmp(s, str_lit("zz")) > 0);
assert(str_case_cmp(s, str_lit("zzzz")) < 0);
assert(str_case_cmp(s, str_null) > 0);
assert(str_case_cmp(str_null, s) < 0);
assert(str_case_cmp(str_null, str_null) == 0);
assert(str_case_cmp(s, str_lit("ZZZ")) == 0);
assert(str_case_cmp(s, str_lit("ZZ")) > 0);
assert(str_case_cmp(s, str_lit("ZZZZ")) < 0);
assert(str_case_eq(s, str_lit("ZZZ")));
assert(str_cmp_ci(s, s) == 0);
assert(str_cmp_ci(s, str_lit("zzz")) == 0);
assert(str_cmp_ci(s, str_lit("zz")) > 0);
assert(str_cmp_ci(s, str_lit("zzzz")) < 0);
assert(str_cmp_ci(s, str_null) > 0);
assert(str_cmp_ci(str_null, s) < 0);
assert(str_cmp_ci(str_null, str_null) == 0);
assert(str_cmp_ci(s, str_lit("ZZZ")) == 0);
assert(str_cmp_ci(s, str_lit("ZZ")) > 0);
assert(str_cmp_ci(s, str_lit("ZZZZ")) < 0);
assert(str_eq_ci(s, str_lit("ZZZ")));

passed;
}
Expand Down Expand Up @@ -265,6 +265,46 @@ void test_composition(void)
passed;
}

static
void test_sort(void)
{
str src[] = { str_lit("z"), str_lit("zzz"), str_lit("aaa"), str_lit("bbb") };

str_sort(str_order_asc, src, sizeof(src)/sizeof(src[0]));

assert(str_eq(src[0], str_lit("aaa")));
assert(str_eq(src[1], str_lit("bbb")));
assert(str_eq(src[2], str_lit("z")));
assert(str_eq(src[3], str_lit("zzz")));

str_sort(str_order_desc, src, sizeof(src)/sizeof(src[0]));

assert(str_eq(src[0], str_lit("zzz")));
assert(str_eq(src[1], str_lit("z")));
assert(str_eq(src[2], str_lit("bbb")));
assert(str_eq(src[3], str_lit("aaa")));
}

static
void test_sort_ci(void)
{
str src[] = { str_lit("ZZZ"), str_lit("zzz"), str_lit("aaa"), str_lit("AAA") };

str_sort(str_order_asc_ci, src, sizeof(src)/sizeof(src[0]));

assert(str_eq_ci(src[0], str_lit("aaa")));
assert(str_eq_ci(src[1], str_lit("aaa")));
assert(str_eq_ci(src[2], str_lit("zzz")));
assert(str_eq_ci(src[3], str_lit("zzz")));

str_sort(str_order_desc_ci, src, sizeof(src)/sizeof(src[0]));

assert(str_eq_ci(src[0], str_lit("zzz")));
assert(str_eq_ci(src[1], str_lit("zzz")));
assert(str_eq_ci(src[2], str_lit("aaa")));
assert(str_eq_ci(src[3], str_lit("aaa")));
}

int main(void)
{
// tests
Expand All @@ -274,12 +314,14 @@ int main(void)
test_str_move();
test_str_ref();
test_str_cmp();
test_str_case_cmp();
test_str_cmp_ci();
test_str_acquire();
test_str_cat();
test_str_join();
test_str_join_ignore_empty();
test_composition();
test_sort();
test_sort_ci();

return puts("OK.") < 0;
}

0 comments on commit d8eff0e

Please sign in to comment.