diff options
-rw-r--r-- | Makefile | 2 | ||||
-rw-r--r-- | include/libwapcaplet/libwapcaplet.h | 75 | ||||
-rw-r--r-- | src/libwapcaplet.c | 23 | ||||
-rw-r--r-- | test/basictests.c | 124 |
4 files changed, 143 insertions, 81 deletions
@@ -6,7 +6,7 @@ # Component settings COMPONENT := wapcaplet -COMPONENT_VERSION := 0.4.0 +COMPONENT_VERSION := 0.4.3 # Default to a static library COMPONENT_TYPE ?= lib-static diff --git a/include/libwapcaplet/libwapcaplet.h b/include/libwapcaplet/libwapcaplet.h index d8cc841..57e2e48 100644 --- a/include/libwapcaplet/libwapcaplet.h +++ b/include/libwapcaplet/libwapcaplet.h @@ -133,7 +133,17 @@ extern lwc_error lwc_string_tolower(lwc_string *str, lwc_string **ret); * @note Use this if copying the string and intending both sides to retain * ownership. */ +#if defined(STMTEXPR) #define lwc_string_ref(str) ({lwc_string *__lwc_s = (str); assert(__lwc_s != NULL); __lwc_s->refcnt++; __lwc_s;}) +#else +static inline lwc_string * +lwc_string_ref(lwc_string *str) +{ + assert(str != NULL); + str->refcnt++; + return str; +} +#endif /** * Release a reference on an lwc_string. @@ -177,6 +187,21 @@ extern void lwc_string_destroy(lwc_string *str); ((*(ret) = ((str1) == (str2))), lwc_error_ok) /** + * Intern a caseless copy of the passed string. + * + * @param str The string to intern the caseless copy of. + * + * @return lwc_error_ok if successful, otherwise the + * error code describing the issue., + * + * @note This is for "internal" use by the caseless comparison + * macro and not for users. + */ +extern lwc_error +lwc__intern_caseless_string(lwc_string *str); + +#if defined(STMTEXPR) +/** * Check if two interned strings are case-insensitively equal. * * @param _str1 The first string in the comparison. @@ -202,19 +227,37 @@ extern void lwc_string_destroy(lwc_string *str); __lwc_err; \ }) +#else /** - * Intern a caseless copy of the passed string. - * - * @param str The string to intern the caseless copy of. - * - * @return lwc_error_ok if successful, otherwise the - * error code describing the issue., + * Check if two interned strings are case-insensitively equal. * - * @note This is for "internal" use by the caseless comparison - * macro and not for users. - */ -extern lwc_error -lwc__intern_caseless_string(lwc_string *str); + * @param str1 The first string in the comparison. + * @param str2 The second string in the comparison. + * @param ret A pointer to a boolean to be filled out with the result. + * @return Result of operation, if not ok then value pointed to by \a ret will + * not be valid. + */ +static inline lwc_error +lwc_string_caseless_isequal(lwc_string *str1, lwc_string *str2, bool *ret) +{ + lwc_error err = lwc_error_ok; + if (str1->insensitive == NULL) { + err = lwc__intern_caseless_string(str1); + } + if (err == lwc_error_ok && str2->insensitive == NULL) { + err = lwc__intern_caseless_string(str2); + } + if (err == lwc_error_ok) + *ret = (str1->insensitive == str2->insensitive); + return err; +} +#endif + +#if defined(STMTEXPR) +#define lwc__assert_and_expr(str, expr) ({assert(str != NULL); expr;}) +#else +#define lwc__assert_and_expr(str, expr) (expr) +#endif /** * Retrieve the data pointer for an interned string. @@ -228,7 +271,7 @@ lwc__intern_caseless_string(lwc_string *str); * in future. Any code relying on it currently should be * modified to use ::lwc_string_length if possible. */ -#define lwc_string_data(str) ({assert(str != NULL); (const char *)((str)+1);}) +#define lwc_string_data(str) lwc__assert_and_expr(str, (const char *)((str)+1)) /** * Retrieve the data length for an interned string. @@ -236,7 +279,7 @@ lwc__intern_caseless_string(lwc_string *str); * @param str The string to retrieve the length of. * @return The length of \a str. */ -#define lwc_string_length(str) ({assert(str != NULL); (str)->len;}) +#define lwc_string_length(str) lwc__assert_and_expr(str, (str)->len) /** * Retrieve (or compute if unavailable) a hash value for the content of the string. @@ -250,7 +293,7 @@ lwc__intern_caseless_string(lwc_string *str); * to be stable between invocations of the program. Never use the hash * value as a way to directly identify the value of the string. */ -#define lwc_string_hash_value(str) ({assert(str != NULL); (str)->hash;}) +#define lwc_string_hash_value(str) lwc__assert_and_expr(str, (str)->hash) /** * Retrieve a hash value for the caseless content of the string. @@ -278,6 +321,10 @@ static inline lwc_error lwc_string_caseless_hash_value( /** * Iterate the context and return every string in it. * + * If there are no strings found in the context, then this has the + * side effect of removing the global context which will reduce the + * chances of false-positives on leak checkers. + * * @param cb The callback to give the string to. * @param pw The private word for the callback. */ diff --git a/src/libwapcaplet.c b/src/libwapcaplet.c index 9521e96..34a72cd 100644 --- a/src/libwapcaplet.c +++ b/src/libwapcaplet.c @@ -47,7 +47,7 @@ static lwc_context *ctx = NULL; typedef lwc_hash (*lwc_hasher)(const char *, size_t); typedef int (*lwc_strncmp)(const char *, const char *, size_t); -typedef void (*lwc_memcpy)(char *, const char *, size_t); +typedef void * (*lwc_memcpy)(void * restrict, const void * restrict, size_t); static lwc_error lwc__initialise(void) @@ -238,12 +238,17 @@ lwc__lcase_strncmp(const char *s1, const char *s2, size_t n) return 0; } -static void -lwc__lcase_memcpy(char *target, const char *source, size_t n) +static void * +lwc__lcase_memcpy(void *restrict _target, const void *restrict _source, size_t n) { + char *restrict target = _target; + const char *restrict source = _source; + while (n--) { *target++ = lwc__dolower(*source++); } + + return _target; } lwc_error @@ -266,12 +271,22 @@ lwc_iterate_strings(lwc_iteration_callback_fn cb, void *pw) { lwc_hash n; lwc_string *str; + bool found = false; if (ctx == NULL) return; for (n = 0; n < ctx->bucketcount; ++n) { - for (str = ctx->buckets[n]; str != NULL; str = str->next) + for (str = ctx->buckets[n]; str != NULL; str = str->next) { + found = true; cb(str, pw); + } + } + + if (found == false) { + /* We found no strings, so remove the global context. */ + free(ctx->buckets); + free(ctx); + ctx = NULL; } } diff --git a/test/basictests.c b/test/basictests.c index 7711da7..48b9beb 100644 --- a/test/basictests.c +++ b/test/basictests.c @@ -43,7 +43,7 @@ END_TEST START_TEST (test_lwc_intern_substring_aborts2) { lwc_string *str; - fail_unless(lwc_intern_string("Jam", 3, &str) == lwc_error_ok, + ck_assert_msg(lwc_intern_string("Jam", 3, &str) == lwc_error_ok, "unable to intern 'Jam'"); lwc_intern_substring(str, 88, 77, null_lwc_p); @@ -59,7 +59,7 @@ END_TEST START_TEST (test_lwc_string_tolower_aborts2) { lwc_string *str; - fail_unless(lwc_intern_string("Badger", 6, &str) == lwc_error_ok, + ck_assert_msg(lwc_intern_string("Badger", 6, &str) == lwc_error_ok, "unable to intern 'Badger'"); lwc_string_tolower(str, null_lwc_p); @@ -115,9 +115,9 @@ with_simple_context_teardown(void) START_TEST (test_lwc_intern_string_ok) { lwc_string *str = NULL; - fail_unless(lwc_intern_string("A", 1, &str) == lwc_error_ok, + ck_assert_msg(lwc_intern_string("A", 1, &str) == lwc_error_ok, "Unable to intern a simple string"); - fail_unless(str != NULL, + ck_assert_msg(str != NULL, "Returned OK but str was still NULL"); } END_TEST @@ -125,13 +125,13 @@ END_TEST START_TEST (test_lwc_intern_string_twice_ok) { lwc_string *str1 = NULL, *str2 = NULL; - fail_unless(lwc_intern_string("A", 1, &str1) == lwc_error_ok, + ck_assert_msg(lwc_intern_string("A", 1, &str1) == lwc_error_ok, "Unable to intern a simple string"); - fail_unless(str1 != NULL, + ck_assert_msg(str1 != NULL, "Returned OK but str was still NULL"); - fail_unless(lwc_intern_string("B", 1, &str2) == lwc_error_ok, + ck_assert_msg(lwc_intern_string("B", 1, &str2) == lwc_error_ok, "Unable to intern a simple string"); - fail_unless(str2 != NULL, + ck_assert_msg(str2 != NULL, "Returned OK but str was still NULL"); } END_TEST @@ -139,13 +139,13 @@ END_TEST START_TEST (test_lwc_intern_string_twice_same_ok) { lwc_string *str1 = NULL, *str2 = NULL; - fail_unless(lwc_intern_string("A", 1, &str1) == lwc_error_ok, + ck_assert_msg(lwc_intern_string("A", 1, &str1) == lwc_error_ok, "Unable to intern a simple string"); - fail_unless(str1 != NULL, + ck_assert_msg(str1 != NULL, "Returned OK but str was still NULL"); - fail_unless(lwc_intern_string("A", 1, &str2) == lwc_error_ok, + ck_assert_msg(lwc_intern_string("A", 1, &str2) == lwc_error_ok, "Unable to intern a simple string"); - fail_unless(str2 != NULL, + ck_assert_msg(str2 != NULL, "Returned OK but str was still NULL"); } END_TEST @@ -157,18 +157,18 @@ static lwc_string *intern_one = NULL, *intern_two = NULL, *intern_three = NULL, static void with_filled_context_setup(void) { - fail_unless(lwc_intern_string("one", 3, &intern_one) == lwc_error_ok, + ck_assert_msg(lwc_intern_string("one", 3, &intern_one) == lwc_error_ok, "Unable to intern 'one'"); - fail_unless(lwc_intern_string("two", 3, &intern_two) == lwc_error_ok, + ck_assert_msg(lwc_intern_string("two", 3, &intern_two) == lwc_error_ok, "Unable to intern 'two'"); - fail_unless(lwc_intern_string("three", 5, &intern_three) == lwc_error_ok, + ck_assert_msg(lwc_intern_string("three", 5, &intern_three) == lwc_error_ok, "Unable to intern 'three'"); - fail_unless(lwc_intern_string("YAY", 3, &intern_YAY) == lwc_error_ok, + ck_assert_msg(lwc_intern_string("YAY", 3, &intern_YAY) == lwc_error_ok, "Unable to intern 'YAY'"); - fail_unless(intern_one != intern_two, "'one' == 'two'"); - fail_unless(intern_one != intern_three, "'one' == 'three'"); - fail_unless(intern_two != intern_three, "'two' == 'three'"); + ck_assert_msg(intern_one != intern_two, "'one' == 'two'"); + ck_assert_msg(intern_one != intern_three, "'one' == 'three'"); + ck_assert_msg(intern_two != intern_three, "'two' == 'three'"); } static void @@ -185,10 +185,10 @@ START_TEST (test_lwc_interning_works) { lwc_string *new_one = NULL; - fail_unless(lwc_intern_string("one", 3, &new_one) == lwc_error_ok, + ck_assert_msg(lwc_intern_string("one", 3, &new_one) == lwc_error_ok, "Unable to re-intern 'one'"); - fail_unless(new_one == intern_one, + ck_assert_msg(new_one == intern_one, "Internalising of the string failed"); } END_TEST @@ -197,12 +197,12 @@ START_TEST (test_lwc_intern_substring) { lwc_string *new_hre = NULL, *sub_hre = NULL; - fail_unless(lwc_intern_string("hre", 3, &new_hre) == lwc_error_ok, + ck_assert_msg(lwc_intern_string("hre", 3, &new_hre) == lwc_error_ok, "Unable to intern 'hre'"); - fail_unless(lwc_intern_substring(intern_three, + ck_assert_msg(lwc_intern_substring(intern_three, 1, 3, &sub_hre) == lwc_error_ok, "Unable to re-intern 'hre' by substring"); - fail_unless(new_hre == sub_hre, + ck_assert_msg(new_hre == sub_hre, "'hre' != 'hre' -- wow!"); } END_TEST @@ -211,7 +211,7 @@ START_TEST (test_lwc_intern_substring_bad_offset) { lwc_string *str; - fail_unless(lwc_intern_substring(intern_three, 100, 1, &str) == lwc_error_range, + ck_assert_msg(lwc_intern_substring(intern_three, 100, 1, &str) == lwc_error_range, "Able to intern substring starting out of range"); } END_TEST @@ -220,14 +220,14 @@ START_TEST (test_lwc_intern_substring_bad_size) { lwc_string *str; - fail_unless(lwc_intern_substring(intern_three, 1, 100, &str) == lwc_error_range, + ck_assert_msg(lwc_intern_substring(intern_three, 1, 100, &str) == lwc_error_range, "Able to intern substring ending out of range"); } END_TEST START_TEST (test_lwc_string_ref_ok) { - fail_unless(lwc_string_ref(intern_one) == intern_one, + ck_assert_msg(lwc_string_ref(intern_one) == intern_one, "Oddly, reffing a string didn't return it"); } END_TEST @@ -249,9 +249,9 @@ END_TEST START_TEST (test_lwc_string_isequal_ok) { bool result = true; - fail_unless((lwc_string_isequal(intern_one, intern_two, &result)) == lwc_error_ok, + ck_assert_msg((lwc_string_isequal(intern_one, intern_two, &result)) == lwc_error_ok, "Failure comparing 'one' and 'two'"); - fail_unless(result == false, + ck_assert_msg(result == false, "'one' == 'two' ?!"); } END_TEST @@ -261,16 +261,16 @@ START_TEST (test_lwc_string_caseless_isequal_ok1) bool result = true; lwc_string *new_ONE; - fail_unless(lwc_intern_string("ONE", 3, &new_ONE) == lwc_error_ok, + ck_assert_msg(lwc_intern_string("ONE", 3, &new_ONE) == lwc_error_ok, "Failure interning 'ONE'"); - fail_unless((lwc_string_isequal(intern_one, new_ONE, &result)) == lwc_error_ok); - fail_unless(result == false, + ck_assert((lwc_string_isequal(intern_one, new_ONE, &result)) == lwc_error_ok); + ck_assert_msg(result == false, "'one' == 'ONE' ?!"); - fail_unless((lwc_string_caseless_isequal(intern_one, new_ONE, &result)) == lwc_error_ok, + ck_assert_msg((lwc_string_caseless_isequal(intern_one, new_ONE, &result)) == lwc_error_ok, "Failure comparing 'one' and 'ONE' caselessly"); - fail_unless(result == true, + ck_assert_msg(result == true, "'one' !~= 'ONE' ?!"); } END_TEST @@ -280,16 +280,16 @@ START_TEST (test_lwc_string_caseless_isequal_ok2) bool result = true; lwc_string *new_yay; - fail_unless(lwc_intern_string("yay", 3, &new_yay) == lwc_error_ok, + ck_assert_msg(lwc_intern_string("yay", 3, &new_yay) == lwc_error_ok, "Failure interning 'yay'"); - fail_unless((lwc_string_isequal(intern_YAY, new_yay, &result)) == lwc_error_ok); - fail_unless(result == false, + ck_assert((lwc_string_isequal(intern_YAY, new_yay, &result)) == lwc_error_ok); + ck_assert_msg(result == false, "'yay' == 'YAY' ?!"); - fail_unless((lwc_string_caseless_isequal(intern_YAY, new_yay, &result)) == lwc_error_ok, + ck_assert_msg((lwc_string_caseless_isequal(intern_YAY, new_yay, &result)) == lwc_error_ok, "Failure comparing 'yay' and 'YAY' caselessly"); - fail_unless(result == true, + ck_assert_msg(result == true, "'yay' !~= 'YAY' ?!"); } END_TEST @@ -298,16 +298,16 @@ START_TEST (test_lwc_string_caseless_isequal_bad) { bool result = true; - fail_unless(lwc_string_caseless_isequal(intern_YAY, intern_one, &result) == lwc_error_ok, + ck_assert_msg(lwc_string_caseless_isequal(intern_YAY, intern_one, &result) == lwc_error_ok, "Failure comparing 'YAY' and 'one' caselessly"); - fail_unless(result == false, + ck_assert_msg(result == false, "'YAY' ~= 'one' ?!"); } END_TEST START_TEST (test_lwc_extract_data_ok) { - fail_unless(memcmp("one", + ck_assert_msg(memcmp("one", lwc_string_data(intern_one), lwc_string_length(intern_one)) == 0, "Extracting data ptr etc failed"); @@ -324,10 +324,10 @@ START_TEST (test_lwc_string_is_nul_terminated) { lwc_string *new_ONE; - fail_unless(lwc_intern_string("ONE", 3, &new_ONE) == lwc_error_ok, + ck_assert_msg(lwc_intern_string("ONE", 3, &new_ONE) == lwc_error_ok, "Failure interning 'ONE'"); - fail_unless(lwc_string_data(new_ONE)[lwc_string_length(new_ONE)] == '\0', + ck_assert_msg(lwc_string_data(new_ONE)[lwc_string_length(new_ONE)] == '\0', "Interned string isn't NUL terminated"); } END_TEST @@ -337,13 +337,13 @@ START_TEST (test_lwc_substring_is_nul_terminated) lwc_string *new_ONE; lwc_string *new_O; - fail_unless(lwc_intern_string("ONE", 3, &new_ONE) == lwc_error_ok, + ck_assert_msg(lwc_intern_string("ONE", 3, &new_ONE) == lwc_error_ok, "Failure interning 'ONE'"); - fail_unless(lwc_intern_substring(new_ONE, 0, 1, &new_O) == lwc_error_ok, + ck_assert_msg(lwc_intern_substring(new_ONE, 0, 1, &new_O) == lwc_error_ok, "Failure interning substring 'O'"); - fail_unless(lwc_string_data(new_O)[lwc_string_length(new_O)] == '\0', + ck_assert_msg(lwc_string_data(new_O)[lwc_string_length(new_O)] == '\0', "Interned substring isn't NUL terminated"); } END_TEST @@ -354,13 +354,13 @@ START_TEST (test_lwc_string_tolower_ok1) lwc_string *new_ONE; lwc_string *new_one; - fail_unless(lwc_intern_string("ONE", 3, &new_ONE) == lwc_error_ok, + ck_assert_msg(lwc_intern_string("ONE", 3, &new_ONE) == lwc_error_ok, "Failure interning 'ONE'"); - fail_unless(lwc_string_tolower(new_ONE, &new_one) == lwc_error_ok); - fail_unless(lwc_string_isequal(intern_one, new_ONE, &result) == lwc_error_ok); - fail_unless(result == false, "'one' == 'ONE' ?!"); - fail_unless(lwc_string_isequal(intern_one, new_one, &result) == lwc_error_ok); - fail_unless(result == true, "'one' != 'one' ?!"); + ck_assert(lwc_string_tolower(new_ONE, &new_one) == lwc_error_ok); + ck_assert(lwc_string_isequal(intern_one, new_ONE, &result) == lwc_error_ok); + ck_assert_msg(result == false, "'one' == 'ONE' ?!"); + ck_assert(lwc_string_isequal(intern_one, new_one, &result) == lwc_error_ok); + ck_assert_msg(result == true, "'one' != 'one' ?!"); } END_TEST @@ -370,16 +370,16 @@ START_TEST (test_lwc_string_tolower_ok2) lwc_string *new_ONE; lwc_string *new_one; - fail_unless(lwc_intern_string("ONE", 3, &new_ONE) == lwc_error_ok, + ck_assert_msg(lwc_intern_string("ONE", 3, &new_ONE) == lwc_error_ok, "Failure interning 'ONE'"); /* Ensure new_ONE has an insensitive pointer set */ - fail_unless(lwc_string_caseless_isequal(intern_one, new_ONE, &result) == lwc_error_ok); - fail_unless(result == true, "'one' != 'ONE' (caseless) ?!"); - fail_unless(lwc_string_tolower(new_ONE, &new_one) == lwc_error_ok); - fail_unless(lwc_string_isequal(intern_one, new_ONE, &result) == lwc_error_ok); - fail_unless(result == false, "'one' == 'ONE' ?!"); - fail_unless(lwc_string_isequal(intern_one, new_one, &result) == lwc_error_ok); - fail_unless(result == true, "'one' != 'one' ?!"); + ck_assert(lwc_string_caseless_isequal(intern_one, new_ONE, &result) == lwc_error_ok); + ck_assert_msg(result == true, "'one' != 'ONE' (caseless) ?!"); + ck_assert(lwc_string_tolower(new_ONE, &new_one) == lwc_error_ok); + ck_assert(lwc_string_isequal(intern_one, new_ONE, &result) == lwc_error_ok); + ck_assert_msg(result == false, "'one' == 'ONE' ?!"); + ck_assert(lwc_string_isequal(intern_one, new_one, &result) == lwc_error_ok); + ck_assert_msg(result == true, "'one' != 'one' ?!"); } END_TEST @@ -395,7 +395,7 @@ START_TEST (test_lwc_string_iteration) { int counter = 0; lwc_iterate_strings(counting_cb, (void*)&counter); - fail_unless(counter == 4, "Incorrect string count"); + ck_assert_msg(counter == 4, "Incorrect string count"); } END_TEST |