summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Drake <tlsa@netsurf-browser.org>2016-01-30 22:26:46 +0000
committerMichael Drake <tlsa@netsurf-browser.org>2016-01-30 22:26:46 +0000
commit436e979d9f9ae8897bf2cc0afc985dbfa09347ba (patch)
treea9542977a466f35ae4fe12faf5c3f4ed020cb9b1
parent7c802fa89776e5a39eb97286020ccc06663f6226 (diff)
parent08c5618a803fa32243dfa434225604566c5dac91 (diff)
downloadlibwapcaplet-436e979d9f9ae8897bf2cc0afc985dbfa09347ba.tar.gz
libwapcaplet-436e979d9f9ae8897bf2cc0afc985dbfa09347ba.tar.bz2
Merge branch 'tlsa/tolower'
-rw-r--r--include/libwapcaplet/libwapcaplet.h10
-rw-r--r--src/libwapcaplet.c295
-rw-r--r--test/basictests.c59
3 files changed, 225 insertions, 139 deletions
diff --git a/include/libwapcaplet/libwapcaplet.h b/include/libwapcaplet/libwapcaplet.h
index 4c0fc71..c0e8c29 100644
--- a/include/libwapcaplet/libwapcaplet.h
+++ b/include/libwapcaplet/libwapcaplet.h
@@ -106,6 +106,16 @@ extern lwc_error lwc_intern_substring(lwc_string *str,
lwc_string **ret);
/**
+ * Optain a lowercased lwc_string from given lwc_string.
+ *
+ * @param str String to create lowercase string from.
+ * @param ret Pointer to ::lwc_string pointer to fill out.
+ * @return Result of operation, if not OK then the value pointed
+ * to by \a ret will not be valid.
+ */
+extern lwc_error lwc_string_tolower(lwc_string *str, lwc_string **ret);
+
+/**
* Increment the reference count on an lwc_string.
*
* This increases the reference count on the given string. You should
diff --git a/src/libwapcaplet.c b/src/libwapcaplet.c
index b82d62f..9521e96 100644
--- a/src/libwapcaplet.c
+++ b/src/libwapcaplet.c
@@ -20,12 +20,11 @@ static inline lwc_hash
lwc__calculate_hash(const char *str, size_t len)
{
lwc_hash z = 0x811c9dc5;
-
while (len > 0) {
z *= 0x01000193;
z ^= *str++;
- len--;
+ len--;
}
return z;
@@ -37,8 +36,8 @@ lwc__calculate_hash(const char *str, size_t len)
#define NR_BUCKETS_DEFAULT (4091)
typedef struct lwc_context_s {
- lwc_string ** buckets;
- lwc_hash bucketcount;
+ lwc_string ** buckets;
+ lwc_hash bucketcount;
} lwc_context;
static lwc_context *ctx = NULL;
@@ -53,136 +52,155 @@ typedef void (*lwc_memcpy)(char *, const char *, size_t);
static lwc_error
lwc__initialise(void)
{
- if (ctx != NULL)
- return lwc_error_ok;
-
- ctx = LWC_ALLOC(sizeof(lwc_context));
-
- if (ctx == NULL)
- return lwc_error_oom;
-
- memset(ctx, 0, sizeof(lwc_context));
-
- ctx->bucketcount = NR_BUCKETS_DEFAULT;
- ctx->buckets = LWC_ALLOC(sizeof(lwc_string *) * ctx->bucketcount);
-
- if (ctx->buckets == NULL) {
- LWC_FREE(ctx);
+ if (ctx != NULL)
+ return lwc_error_ok;
+
+ ctx = LWC_ALLOC(sizeof(lwc_context));
+
+ if (ctx == NULL)
+ return lwc_error_oom;
+
+ memset(ctx, 0, sizeof(lwc_context));
+
+ ctx->bucketcount = NR_BUCKETS_DEFAULT;
+ ctx->buckets = LWC_ALLOC(sizeof(lwc_string *) * ctx->bucketcount);
+
+ if (ctx->buckets == NULL) {
+ LWC_FREE(ctx);
ctx = NULL;
- return lwc_error_oom;
- }
-
- memset(ctx->buckets, 0, sizeof(lwc_string *) * ctx->bucketcount);
-
- return lwc_error_ok;
+ return lwc_error_oom;
+ }
+
+ memset(ctx->buckets, 0, sizeof(lwc_string *) * ctx->bucketcount);
+
+ return lwc_error_ok;
}
static lwc_error
lwc__intern(const char *s, size_t slen,
- lwc_string **ret,
- lwc_hasher hasher,
- lwc_strncmp compare,
- lwc_memcpy copy)
+ lwc_string **ret,
+ lwc_hasher hasher,
+ lwc_strncmp compare,
+ lwc_memcpy copy)
{
- lwc_hash h;
- lwc_hash bucket;
- lwc_string *str;
- lwc_error eret;
-
- assert((s != NULL) || (slen == 0));
- assert(ret);
-
- if (ctx == NULL) {
- eret = lwc__initialise();
- if (eret != lwc_error_ok)
- return eret;
- if (ctx == NULL)
- return lwc_error_oom;
- }
-
- h = hasher(s, slen);
- bucket = h % ctx->bucketcount;
- str = ctx->buckets[bucket];
-
- while (str != NULL) {
- if ((str->hash == h) && (str->len == slen)) {
- if (compare(CSTR_OF(str), s, slen) == 0) {
- str->refcnt++;
- *ret = str;
- return lwc_error_ok;
- }
- }
- str = str->next;
- }
-
- /* Add one for the additional NUL. */
- *ret = str = LWC_ALLOC(sizeof(lwc_string) + slen + 1);
-
- if (str == NULL)
- return lwc_error_oom;
-
- str->prevptr = &(ctx->buckets[bucket]);
- str->next = ctx->buckets[bucket];
- if (str->next != NULL)
- str->next->prevptr = &(str->next);
- ctx->buckets[bucket] = str;
-
- str->len = slen;
- str->hash = h;
- str->refcnt = 1;
- str->insensitive = NULL;
-
- copy(STR_OF(str), s, slen);
-
- /* Guarantee NUL termination */
- STR_OF(str)[slen] = '\0';
-
- return lwc_error_ok;
+ lwc_hash h;
+ lwc_hash bucket;
+ lwc_string *str;
+ lwc_error eret;
+
+ assert((s != NULL) || (slen == 0));
+ assert(ret);
+
+ if (ctx == NULL) {
+ eret = lwc__initialise();
+ if (eret != lwc_error_ok)
+ return eret;
+ if (ctx == NULL)
+ return lwc_error_oom;
+ }
+
+ h = hasher(s, slen);
+ bucket = h % ctx->bucketcount;
+ str = ctx->buckets[bucket];
+
+ while (str != NULL) {
+ if ((str->hash == h) && (str->len == slen)) {
+ if (compare(CSTR_OF(str), s, slen) == 0) {
+ str->refcnt++;
+ *ret = str;
+ return lwc_error_ok;
+ }
+ }
+ str = str->next;
+ }
+
+ /* Add one for the additional NUL. */
+ *ret = str = LWC_ALLOC(sizeof(lwc_string) + slen + 1);
+
+ if (str == NULL)
+ return lwc_error_oom;
+
+ str->prevptr = &(ctx->buckets[bucket]);
+ str->next = ctx->buckets[bucket];
+ if (str->next != NULL)
+ str->next->prevptr = &(str->next);
+ ctx->buckets[bucket] = str;
+
+ str->len = slen;
+ str->hash = h;
+ str->refcnt = 1;
+ str->insensitive = NULL;
+
+ copy(STR_OF(str), s, slen);
+
+ /* Guarantee NUL termination */
+ STR_OF(str)[slen] = '\0';
+
+ return lwc_error_ok;
}
lwc_error
lwc_intern_string(const char *s, size_t slen,
- lwc_string **ret)
+ lwc_string **ret)
{
- return lwc__intern(s, slen, ret,
- lwc__calculate_hash,
- strncmp, (lwc_memcpy)memcpy);
+ return lwc__intern(s, slen, ret,
+ lwc__calculate_hash,
+ strncmp, (lwc_memcpy)memcpy);
}
lwc_error
lwc_intern_substring(lwc_string *str,
- size_t ssoffset, size_t sslen,
- lwc_string **ret)
+ size_t ssoffset, size_t sslen,
+ lwc_string **ret)
+{
+ assert(str);
+ assert(ret);
+
+ if (ssoffset >= str->len)
+ return lwc_error_range;
+ if ((ssoffset + sslen) > str->len)
+ return lwc_error_range;
+
+ return lwc_intern_string(CSTR_OF(str) + ssoffset, sslen, ret);
+}
+
+lwc_error
+lwc_string_tolower(lwc_string *str, lwc_string **ret)
{
- assert(str);
- assert(ret);
-
- if (ssoffset >= str->len)
- return lwc_error_range;
- if ((ssoffset + sslen) > str->len)
- return lwc_error_range;
-
- return lwc_intern_string(CSTR_OF(str) + ssoffset, sslen, ret);
+ assert(str);
+ assert(ret);
+
+ /* Internally make use of knowledge that insensitive strings
+ * are lower case. */
+ if (str->insensitive == NULL) {
+ lwc_error error = lwc__intern_caseless_string(str);
+ if (error != lwc_error_ok) {
+ return error;
+ }
+ }
+
+ *ret = lwc_string_ref(str->insensitive);
+ return lwc_error_ok;
}
void
lwc_string_destroy(lwc_string *str)
{
- assert(str);
-
- *(str->prevptr) = str->next;
-
- if (str->next != NULL)
- str->next->prevptr = str->prevptr;
+ assert(str);
+
+ *(str->prevptr) = str->next;
+
+ if (str->next != NULL)
+ str->next->prevptr = str->prevptr;
- if (str->insensitive != NULL && str->refcnt == 0)
- lwc_string_unref(str->insensitive);
+ if (str->insensitive != NULL && str->refcnt == 0)
+ lwc_string_unref(str->insensitive);
#ifndef NDEBUG
- memset(str, 0xA5, sizeof(*str) + str->len);
+ memset(str, 0xA5, sizeof(*str) + str->len);
#endif
-
- LWC_FREE(str);
+
+ LWC_FREE(str);
}
/**** Shonky caseless bits ****/
@@ -190,21 +208,20 @@ lwc_string_destroy(lwc_string *str)
static inline char
lwc__dolower(const char c)
{
- if (c >= 'A' && c <= 'Z')
- return c + 'a' - 'A';
- return c;
+ if (c >= 'A' && c <= 'Z')
+ return c + 'a' - 'A';
+ return c;
}
static inline lwc_hash
lwc__calculate_lcase_hash(const char *str, size_t len)
{
lwc_hash z = 0x811c9dc5;
-
while (len > 0) {
z *= 0x01000193;
z ^= lwc__dolower(*str++);
- len--;
+ len--;
}
return z;
@@ -213,33 +230,33 @@ lwc__calculate_lcase_hash(const char *str, size_t len)
static int
lwc__lcase_strncmp(const char *s1, const char *s2, size_t n)
{
- while (n--) {
- if (*s1++ != lwc__dolower(*s2++))
- /** @todo Test this somehow? */
- return 1;
- }
- return 0;
+ while (n--) {
+ if (*s1++ != lwc__dolower(*s2++))
+ /** @todo Test this somehow? */
+ return 1;
+ }
+ return 0;
}
static void
lwc__lcase_memcpy(char *target, const char *source, size_t n)
{
- while (n--) {
- *target++ = lwc__dolower(*source++);
- }
+ while (n--) {
+ *target++ = lwc__dolower(*source++);
+ }
}
lwc_error
lwc__intern_caseless_string(lwc_string *str)
{
- assert(str);
- assert(str->insensitive == NULL);
-
- return lwc__intern(CSTR_OF(str),
- str->len, &(str->insensitive),
- lwc__calculate_lcase_hash,
- lwc__lcase_strncmp,
- lwc__lcase_memcpy);
+ assert(str);
+ assert(str->insensitive == NULL);
+
+ return lwc__intern(CSTR_OF(str),
+ str->len, &(str->insensitive),
+ lwc__calculate_lcase_hash,
+ lwc__lcase_strncmp,
+ lwc__lcase_memcpy);
}
/**** Iteration ****/
@@ -247,14 +264,14 @@ lwc__intern_caseless_string(lwc_string *str)
void
lwc_iterate_strings(lwc_iteration_callback_fn cb, void *pw)
{
- lwc_hash n;
- lwc_string *str;
-
+ lwc_hash n;
+ lwc_string *str;
+
if (ctx == NULL)
return;
-
- for (n = 0; n < ctx->bucketcount; ++n) {
- for (str = ctx->buckets[n]; str != NULL; str = str->next)
- cb(str, pw);
- }
+
+ for (n = 0; n < ctx->bucketcount; ++n) {
+ for (str = ctx->buckets[n]; str != NULL; str = str->next)
+ cb(str, pw);
+ }
}
diff --git a/test/basictests.c b/test/basictests.c
index ec9bf20..c23b547 100644
--- a/test/basictests.c
+++ b/test/basictests.c
@@ -50,6 +50,22 @@ START_TEST (test_lwc_intern_substring_aborts2)
}
END_TEST
+START_TEST (test_lwc_string_tolower_aborts1)
+{
+ lwc_string_tolower(null_lwc, null_lwc_p);
+}
+END_TEST
+
+START_TEST (test_lwc_string_tolower_aborts2)
+{
+ lwc_string *str;
+ fail_unless(lwc_intern_string("Badger", 6, &str) == lwc_error_ok,
+ "unable to intern 'Badger'");
+
+ lwc_string_tolower(str, null_lwc_p);
+}
+END_TEST
+
START_TEST (test_lwc_string_ref_aborts)
{
lwc_string_ref(null_lwc);
@@ -330,6 +346,41 @@ START_TEST (test_lwc_substring_is_nul_terminated)
}
END_TEST
+START_TEST (test_lwc_string_tolower_ok1)
+{
+ bool result = true;
+ lwc_string *new_ONE;
+ lwc_string *new_one;
+
+ fail_unless(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' ?!");
+}
+END_TEST
+
+START_TEST (test_lwc_string_tolower_ok2)
+{
+ bool result = true;
+ lwc_string *new_ONE;
+ lwc_string *new_one;
+
+ fail_unless(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' ?!");
+}
+END_TEST
+
static void
counting_cb(lwc_string *str, void *pw)
{
@@ -368,6 +419,12 @@ lwc_basic_suite(SRunner *sr)
test_lwc_intern_substring_aborts2,
SIGABRT);
tcase_add_test_raise_signal(tc_basic,
+ test_lwc_string_tolower_aborts1,
+ SIGABRT);
+ tcase_add_test_raise_signal(tc_basic,
+ test_lwc_string_tolower_aborts2,
+ SIGABRT);
+ tcase_add_test_raise_signal(tc_basic,
test_lwc_string_ref_aborts,
SIGABRT);
tcase_add_test_raise_signal(tc_basic,
@@ -408,6 +465,8 @@ lwc_basic_suite(SRunner *sr)
tcase_add_test(tc_basic, test_lwc_string_caseless_isequal_ok1);
tcase_add_test(tc_basic, test_lwc_string_caseless_isequal_ok2);
tcase_add_test(tc_basic, test_lwc_string_caseless_isequal_bad);
+ tcase_add_test(tc_basic, test_lwc_string_tolower_ok1);
+ tcase_add_test(tc_basic, test_lwc_string_tolower_ok2);
tcase_add_test(tc_basic, test_lwc_extract_data_ok);
tcase_add_test(tc_basic, test_lwc_string_hash_value_ok);
tcase_add_test(tc_basic, test_lwc_string_is_nul_terminated);