diff options
Diffstat (limited to 'common')
-rw-r--r-- | common/Makefile.am | 11 | ||||
-rw-r--r-- | common/array.c | 119 | ||||
-rw-r--r-- | common/array.h | 66 | ||||
-rw-r--r-- | common/compat.c | 95 | ||||
-rw-r--r-- | common/compat.h | 123 | ||||
-rw-r--r-- | common/debug.c | 151 | ||||
-rw-r--r-- | common/debug.h | 129 | ||||
-rw-r--r-- | common/dict.c | 391 | ||||
-rw-r--r-- | common/dict.h | 178 | ||||
-rw-r--r-- | common/library.c | 286 | ||||
-rw-r--r-- | common/library.h | 80 | ||||
-rw-r--r-- | common/tests/Makefile.am | 32 | ||||
-rw-r--r-- | common/tests/test-array.c | 194 | ||||
-rw-r--r-- | common/tests/test-dict.c | 465 |
14 files changed, 2320 insertions, 0 deletions
diff --git a/common/Makefile.am b/common/Makefile.am new file mode 100644 index 0000000..f37a501 --- /dev/null +++ b/common/Makefile.am @@ -0,0 +1,11 @@ +NULL = + +SUBDIRS = . tests + +EXTRA_DIST = \ + array.c array.h \ + compat.c compat.h \ + debug.c debug.h \ + dict.c dict.h \ + library.c library.h \ + $(NULL) diff --git a/common/array.c b/common/array.c new file mode 100644 index 0000000..f645fd1 --- /dev/null +++ b/common/array.c @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2011 Collabora Ltd. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * * Redistributions in binary form must reproduce the + * above copyright notice, this list of conditions and + * the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * * The names of contributors to this software may not be + * used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + */ + +#include "config.h" + +#include "array.h" + +#include <stdlib.h> +#include <string.h> + +static int +maybe_expand_array (p11_array *array, + unsigned int length) +{ + unsigned int new_allocated; + void **new_memory; + + if (length <= array->allocated) + return 1; + + new_allocated = array->allocated + 16; + if (new_allocated < length) + new_allocated = length; + + new_memory = realloc (array->elem, new_allocated * sizeof (void*)); + if (new_memory == NULL) + return 0; + + array->elem = new_memory; + array->allocated = new_allocated; + return 1; +} + +p11_array * +p11_array_new (p11_destroyer destroyer) +{ + p11_array *array; + + array = calloc (1, sizeof (p11_array)); + if (array == NULL) + return NULL; + + if (!maybe_expand_array (array, 2)) { + p11_array_free (array); + return NULL; + } + + array->destroyer = destroyer; + return array; +} + +void +p11_array_free (p11_array *array) +{ + unsigned int i; + + if (array == NULL) + return; + + if (array->destroyer) { + for (i = 0; i < array->num; i++) + (array->destroyer) (array->elem[i]); + } + + free (array->elem); + free (array); +} + +int +p11_array_push (p11_array *array, + void *value) +{ + if (!maybe_expand_array (array, array->num + 1)) + return 0; + + array->elem[array->num] = value; + array->num++; + return 1; +} + +void +p11_array_remove (p11_array *array, + unsigned int index) +{ + if (array->destroyer) + (array->destroyer) (array->elem[index]); + memmove (array->elem + index, array->elem + index + 1, + (array->num - (index + 1)) * sizeof (void*)); + array->num--; +} diff --git a/common/array.h b/common/array.h new file mode 100644 index 0000000..6ed7866 --- /dev/null +++ b/common/array.h @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2011 Collabora Ltd. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * * Redistributions in binary form must reproduce the + * above copyright notice, this list of conditions and + * the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * * The names of contributors to this software may not be + * used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * Author: Stef Waler <stefw@collabora.co.uk> + */ + +#ifndef __P11_ARRAY_H__ +#define __P11_ARRAY_H__ + +#include <sys/types.h> + +#ifndef P11_DESTROYER_DEFINED +#define P11_DESTROYER_DEFINED + +typedef void (*p11_destroyer) (void *data); + +#endif + +typedef struct _p11_array { + void **elem; + unsigned int num; + + /* private */ + unsigned int allocated; + p11_destroyer destroyer; +} p11_array; + +p11_array * p11_array_new (p11_destroyer destroyer); + +void p11_array_free (p11_array *array); + +int p11_array_push (p11_array *array, + void *value); + +void p11_array_remove (p11_array *array, + unsigned int index); + +#endif /* __P11_ARRAY_H__ */ diff --git a/common/compat.c b/common/compat.c index 0f7cb78..fe16e07 100644 --- a/common/compat.c +++ b/common/compat.c @@ -36,6 +36,7 @@ #include "compat.h" +#include <assert.h> #include <stdlib.h> #include <string.h> @@ -110,6 +111,80 @@ getprogname (void) #endif /* HAVE_GETPROGNAME */ +#ifdef OS_UNIX + +void +p11_mutex_init (p11_mutex_t *mutex) +{ + pthread_mutexattr_t attr; + int ret; + + pthread_mutexattr_init (&attr); + pthread_mutexattr_settype (&attr, PTHREAD_MUTEX_RECURSIVE); + ret = pthread_mutex_init (mutex, &attr); + assert (ret == 0); + pthread_mutexattr_destroy (&attr); +} + +#endif /* OS_UNIX */ + +#ifdef OS_WIN32 + +const char * +p11_module_error (void) +{ + DWORD code = GetLastError(); + p11_local *local; + LPVOID msg_buf; + + local = p11_library_get_thread_local (); + + FormatMessageA (FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, code, + MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPSTR)&msg_buf, 0, NULL); + + if (local->last_error) + LocalFree (local->last_error); + local->last_error = msg_buf; + + return msg_buf; +} + +int +p11_thread_create (p11_thread_t *thread, + p11_thread_routine routine, + void *arg) +{ + assert (thread); + + *thread = CreateThread (NULL, 0, + (LPTHREAD_START_ROUTINE)routine, + arg, 0, NULL); + + if (*thread == NULL) + return GetLastError (); + + return 0; +} + +int +p11_thread_join (p11_thread_t thread) +{ + DWORD res; + + res = WaitForSingleObject (thread, INFINITE); + if (res == WAIT_FAILED) + return GetLastError (); + + CloseHandle (thread); + return 0; +} + +#endif /* OS_WIN32 */ + #ifndef HAVE_ERR_H #include <stdlib.h> @@ -273,3 +348,23 @@ vwarnx (const char *fmt, } #endif /* HAVE_ERR_H */ + +#ifndef HAVE_MEMDUP + +void * +memdup (void *data, + size_t length) +{ + void *dup; + + if (!data) + return NULL; + + dup = malloc (length); + if (dup != NULL) + memcpy (dup, data, length); + + return dup; +} + +#endif /* HAVE_MEMDUP */ diff --git a/common/compat.h b/common/compat.h index 8b3ac21..5061b9f 100644 --- a/common/compat.h +++ b/common/compat.h @@ -37,6 +37,8 @@ #include "config.h" +#include <sys/types.h> + #if !defined(__cplusplus) && (__GNUC__ > 2) #define GNUC_PRINTF(x, y) __attribute__((__format__(__printf__, x, y))) #else @@ -53,6 +55,120 @@ const char * getprogname (void); #endif +/* ----------------------------------------------------------------------------- + * WIN32 + */ + +#ifdef OS_WIN32 + +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x500 +#endif + +#ifndef _WIN32_IE +#define _WIN32_IE 0x500 +#endif + +#define WIN32_LEAN_AND_MEAN 1 +#include <windows.h> + +/* Oh ... my ... god */ +#undef CreateMutex + +typedef CRITICAL_SECTION p11_mutex_t; + +typedef HANDLE p11_thread_t; + +typedef DWORD p11_thread_id_t; + +#define p11_mutex_init(m) \ + (InitializeCriticalSection (m)) +#define p11_mutex_lock(m) \ + (EnterCriticalSection (m)) +#define p11_mutex_unlock(m) \ + (LeaveCriticalSection (m)) +#define p11_mutex_uninit(m) \ + (DeleteCriticalSection (m)) + +typedef void * (*p11_thread_routine) (void *arg); + +int p11_thread_create (thread_t *thread, thread_routine, void *arg); + +int p11_thread_join (thread_t thread); + +/* Returns a thread_id_t */ +#define p11_thread_id_self() \ + (GetCurrentThreadId ()) + +typedef HMODULE dl_module_t; + +#define p11_module_open(f) \ + (LoadLibrary (f)) +#define p11_module_close(d) \ + (FreeLibrary (d)) +#define p11_module_symbol(d, s) \ + ((void *)GetProcAddress ((d), (s))) + +const char * p11_module_error (void); + +#define p11_sleep_ms(ms) \ + (Sleep (ms)) + +#endif /* OS_WIN32 */ + +/* ---------------------------------------------------------------------------- + * UNIX + */ + +#ifdef OS_UNIX + +#include <pthread.h> +#include <dlfcn.h> +#include <time.h> + +typedef pthread_mutex_t p11_mutex_t; + +void p11_mutex_init (p11_mutex_t *mutex); + +#define p11_mutex_lock(m) \ + (pthread_mutex_lock (m)) +#define p11_mutex_unlock(m) \ + (pthread_mutex_unlock (m)) +#define p11_mutex_uninit(m) \ + (pthread_mutex_destroy(m)) + +typedef pthread_t p11_thread_t; + +typedef pthread_t p11_thread_id_t; + +typedef void * (*p11_thread_routine) (void *arg); + +#define p11_thread_create(t, r, a) \ + (pthread_create ((t), NULL, (r), (a))) +#define p11_thread_join(t) \ + (pthread_join ((t), NULL)) +#define p11_thread_id_self(m) \ + (pthread_self ()) + +typedef void * dl_module_t; + +#define p11_module_open(f) \ + (dlopen ((f), RTLD_LOCAL | RTLD_NOW)) +#define p11_module_close(d) \ + (dlclose(d)) +#define p11_module_error() \ + (dlerror ()) +#define p11_module_symbol(d, s) \ + (dlsym ((d), (s))) + +#define p11_sleep_ms(ms) \ + do { int _ms = (ms); \ + struct timespec _ts = { _ms / 1000, (_ms % 1000) * 1000 * 1000 }; \ + nanosleep (&_ts, NULL); \ + } while(0) + +#endif /* OS_UNIX */ + #ifdef HAVE_ERR_H #include <err.h> @@ -80,4 +196,11 @@ void vwarnx (const char *fmt, va_list ap); #include <errno.h> #endif /* HAVE_ERRNO_H */ +#ifndef HAVE_MEMDUP + +void * memdup (void *data, + size_t length); + +#endif /* HAVE_MEMDUP */ + #endif /* __COMPAT_H__ */ diff --git a/common/debug.c b/common/debug.c new file mode 100644 index 0000000..cca9aaf --- /dev/null +++ b/common/debug.c @@ -0,0 +1,151 @@ +/* + * Copyright (c) 2011 Collabora Ltd. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * * Redistributions in binary form must reproduce the + * above copyright notice, this list of conditions and + * the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * * The names of contributors to this software may not be + * used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * + * CONTRIBUTORS + * Stef Walter <stef@memberwebs.com> + */ + +#include "config.h" + +#include "debug.h" + +#include <assert.h> +#include <stdio.h> +#include <stdarg.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +struct DebugKey { + const char *name; + int value; +}; + +static struct DebugKey debug_keys[] = { + { "lib", P11_DEBUG_LIB }, + { "conf", P11_DEBUG_CONF }, + { "uri", P11_DEBUG_URI }, + { "proxy", P11_DEBUG_PROXY }, + { 0, } +}; + +static int debug_inited = 0; +static int debug_strict = 0; + +/* global variable exported in debug.h */ +int p11_debug_current_flags = ~0; + +static int +parse_environ_flags (void) +{ + const char *env; + int result = 0; + const char *p; + const char *q; + int i; + + env = getenv ("P11_KIT_STRICT"); + if (env && env[0] != '\0') + debug_strict = 1; + + env = getenv ("P11_KIT_DEBUG"); + if (!env) + return 0; + + if (strcmp (env, "all") == 0) { + for (i = 0; debug_keys[i].name; i++) + result |= debug_keys[i].value; + + } else if (strcmp (env, "help") == 0) { + fprintf (stderr, "Supported debug values:"); + for (i = 0; debug_keys[i].name; i++) + fprintf (stderr, " %s", debug_keys[i].name); + fprintf (stderr, "\n"); + + } else { + p = env; + while (*p) { + q = strpbrk (p, ":;, \t"); + if (!q) + q = p + strlen (p); + + for (i = 0; debug_keys[i].name; i++) { + if (q - p == strlen (debug_keys[i].name) && + strncmp (debug_keys[i].name, p, q - p) == 0) + result |= debug_keys[i].value; + } + + p = q; + if (*p) + p++; + } + } + + return result; +} + +void +p11_debug_init (void) +{ + p11_debug_current_flags = parse_environ_flags (); + debug_inited = 1; +} + +void +p11_debug_message (int flag, + const char *format, ...) +{ + char buffer[512]; + va_list args; + + if (flag & p11_debug_current_flags) { + va_start (args, format); + vsnprintf (buffer, sizeof (buffer), format, args); + buffer[sizeof (buffer) -1] = 0; + va_end (args); + fprintf (stderr, "(p11-kit:%d) %s\n", getpid(), buffer); + } +} + +void +p11_debug_precond (const char *format, + ...) +{ + va_list va; + + va_start (va, format); + vfprintf (stderr, format, va); + va_end (va); + + if (debug_strict) + abort (); +} diff --git a/common/debug.h b/common/debug.h new file mode 100644 index 0000000..e4759d4 --- /dev/null +++ b/common/debug.h @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2011 Collabora Ltd. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * * Redistributions in binary form must reproduce the + * above copyright notice, this list of conditions and + * the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * * The names of contributors to this software may not be + * used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * Author: Stef Walter <stefw@collabora.co.uk> + */ + +#ifndef P11_DEBUG_H +#define P11_DEBUG_H + +#include "compat.h" + +/* Please keep this enum in sync with keys in debug.c */ +enum { + P11_DEBUG_LIB = 1 << 1, + P11_DEBUG_CONF = 1 << 2, + P11_DEBUG_URI = 1 << 3, + P11_DEBUG_PROXY = 1 << 4, +}; + +extern int p11_debug_current_flags; + +void p11_debug_init (void); + +void p11_debug_message (int flag, + const char *format, + ...) GNUC_PRINTF (2, 3); + +void p11_debug_precond (const char *format, + ...) GNUC_PRINTF (1, 2); + +#define assert_not_reached() \ + (assert (0 && "this code should not be reached")) + +#define return_val_if_fail(x, v) \ + do { if (!(x)) { \ + p11_debug_precond ("p11-kit: '%s' not true at %s\n", #x, __func__); \ + return v; \ + } } while (0) + +#define return_if_fail(x) \ + do { if (!(x)) { \ + p11_debug_precond ("p11-kit: '%s' not true at %s\n", #x, __func__); \ + return; \ + } } while (0) + +#define return_if_reached() \ + do { \ + p11_debug_precond ("p11-kit: shouldn't be reached at %s\n", __func__); \ + return; \ + } while (0) + +#define return_val_if_reached(v) \ + do { \ + p11_debug_precond ("p11-kit: shouldn't be reached at %s\n", __func__); \ + return v; \ + } while (0) + +#endif /* DEBUG_H */ + +/* ----------------------------------------------------------------------------- + * Below this point is outside the DEBUG_H guard - so it can take effect + * more than once. So you can do: + * + * #define P11_DEBUG_FLAG P11_DEBUG_ONE_THING + * #include "debug.h" + * ... + * p11_debug ("if we're debugging one thing"); + * ... + * #undef P11_DEBUG_FLAG + * #define P11_DEBUG_FLAG DEBUG_OTHER_THING + * #include "debug.h" + * ... + * p11_debug ("if we're debugging the other thing"); + * ... + */ + +#ifdef P11_DEBUG_FLAG +#ifdef WITH_DEBUG + +#undef p11_debug +#define p11_debug(format, ...) do { \ + if (P11_DEBUG_FLAG & p11_debug_current_flags) \ + p11_debug_message (P11_DEBUG_FLAG, "%s: " format, __PRETTY_FUNCTION__, ##__VA_ARGS__); \ + } while (0) + +#undef p11_debugging +#define p11_debugging \ + (P11_DEBUG_FLAG & p11_debug_current_flags) + +#else /* !defined (WITH_DEBUG) */ + +#undef p11_debug +#define p11_debug(format, ...) \ + do {} while (0) + +#undef p11_debugging +#define p11_debugging 0 + +#endif /* !defined (WITH_DEBUG) */ + +#endif /* defined (P11_DEBUG_FLAG) */ diff --git a/common/dict.c b/common/dict.c new file mode 100644 index 0000000..2f976c1 --- /dev/null +++ b/common/dict.c @@ -0,0 +1,391 @@ +/* + * Copyright (c) 2004 Stefan Walter + * Copyright (c) 2011 Collabora Ltd. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * * Redistributions in binary form must reproduce the + * above copyright notice, this list of conditions and + * the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * * The names of contributors to this software may not be + * used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + */ + +#include "config.h" + +#include "dict.h" + +#include <sys/types.h> + +#include <assert.h> +#include <stdlib.h> +#include <string.h> + +struct _p11_dict { + p11_dict_hasher hash_func; + p11_dict_equals equal_func; + p11_destroyer key_destroy_func; + p11_destroyer value_destroy_func; + + struct _p11_dictbucket **buckets; + unsigned int num_items; + unsigned int num_buckets; +}; + +typedef struct _p11_dictbucket { + void *key; + unsigned int hashed; + void *value; + struct _p11_dictbucket *next; +} dictbucket; + +static dictbucket * +next_entry (p11_dictiter *iter) +{ + dictbucket *bucket = iter->next; + while (!bucket) { + if (iter->index >= iter->dict->num_buckets) + return NULL; + bucket = iter->dict->buckets[iter->index++]; + } + iter->next = bucket->next; + return bucket; +} + + +int +p11_dict_next (p11_dictiter *iter, + void **key, + void **value) +{ + dictbucket *bucket = next_entry (iter); + if (bucket == NULL) + return 0; + if (key) + *key = bucket->key; + if (value) + *value = bucket->value; + return 1; +} + +void +p11_dict_iterate (p11_dict *dict, + p11_dictiter *iter) +{ + iter->dict = dict; + iter->index = 0; + iter->next = NULL; +} + +static dictbucket ** +lookup_or_create_bucket (p11_dict *dict, + const void *key, + int create) +{ + dictbucket **bucketp; + unsigned int hash; + + /* Perform the hashing */ + hash = dict->hash_func (key); + + /* scan linked list */ + for (bucketp = &dict->buckets[hash % dict->num_buckets]; + *bucketp != NULL; bucketp = &(*bucketp)->next) { + if((*bucketp)->hashed == hash && dict->equal_func ((*bucketp)->key, key)) + break; + } + + if ((*bucketp) != NULL || !create) + return bucketp; + + /* add a new entry for non-NULL val */ + (*bucketp) = calloc (sizeof (dictbucket), 1); + + if (*bucketp != NULL) { + (*bucketp)->key = (void*)key; + (*bucketp)->hashed = hash; + dict->num_items++; + } + + return bucketp; +} + +void * +p11_dict_get (p11_dict *dict, + const void *key) +{ + dictbucket **bucketp; + + bucketp = lookup_or_create_bucket (dict, key, 0); + if (bucketp && *bucketp) + return (void*)((*bucketp)->value); + else + return NULL; +} + +int +p11_dict_set (p11_dict *dict, + void *key, + void *val) +{ + dictbucket **bucketp; + p11_dictiter iter; + dictbucket *bucket; + dictbucket **new_buckets; + unsigned int num_buckets; + + bucketp = lookup_or_create_bucket (dict, key, 1); + if(bucketp && *bucketp) { + + /* Destroy the previous key */ + if ((*bucketp)->key && (*bucketp)->key != key && dict->key_destroy_func) + dict->key_destroy_func ((*bucketp)->key); + + /* Destroy the previous value */ + if ((*bucketp)->value && (*bucketp)->value != val && dict->value_destroy_func) + dict->value_destroy_func ((*bucketp)->value); + + /* replace entry */ + (*bucketp)->key = key; + (*bucketp)->value = val; + + /* check that the collision rate isn't too high */ + if (dict->num_items > dict->num_buckets) { + num_buckets = dict->num_buckets * 2 + 1; + new_buckets = (dictbucket **)calloc (sizeof (dictbucket *), num_buckets); + + /* Ignore failures, maybe we can expand later */ + if(new_buckets) { + p11_dict_iterate (dict, &iter); + while ((bucket = next_entry (&iter)) != NULL) { + unsigned int i = bucket->hashed % num_buckets; + bucket->next = new_buckets[i]; + new_buckets[i] = bucket; + } + + free (dict->buckets); + dict->buckets = new_buckets; + dict->num_buckets = num_buckets; + } + } + + return 1; + } + + return 0; +} + +int +p11_dict_steal (p11_dict *dict, + const void *key, + void **stolen_key, + void **stolen_value) +{ + dictbucket **bucketp; + + bucketp = lookup_or_create_bucket (dict, key, 0); + if (bucketp && *bucketp) { + dictbucket *old = *bucketp; + *bucketp = (*bucketp)->next; + --dict->num_items; + if (stolen_key) + *stolen_key = old->key; + if (stolen_value) + *stolen_value = old->value; + free (old); + return 1; + } + + return 0; + +} + +int +p11_dict_remove (p11_dict *dict, + const void *key) +{ + void *old_key; + void *old_value; + + if (!p11_dict_steal (dict, key, &old_key, &old_value)) + return 0; + + if (dict->key_destroy_func) + dict->key_destroy_func (old_key); + if (dict->value_destroy_func) + dict->value_destroy_func (old_value); + return 1; +} + +void +p11_dict_clear (p11_dict *dict) +{ + dictbucket *bucket, *next; + int i; + + /* Free all entries in the array */ + for (i = 0; i < dict->num_buckets; ++i) { + bucket = dict->buckets[i]; + while (bucket != NULL) { + next = bucket->next; + if (dict->key_destroy_func) + dict->key_destroy_func (bucket->key); + if (dict->value_destroy_func) + dict->value_destroy_func (bucket->value); + free (bucket); + bucket = next; + } + } + + memset (dict->buckets, 0, dict->num_buckets * sizeof (dictbucket *)); + dict->num_items = 0; +} + +p11_dict * +p11_dict_new (p11_dict_hasher hash_func, + p11_dict_equals equal_func, + p11_destroyer key_destroy_func, + p11_destroyer value_destroy_func) +{ + p11_dict *dict; + + assert (hash_func); + assert (equal_func); + + dict = malloc (sizeof (p11_dict)); + if (dict) { + dict->hash_func = hash_func; + dict->equal_func = equal_func; + dict->key_destroy_func = key_destroy_func; + dict->value_destroy_func = value_destroy_func; + + dict->num_buckets = 9; + dict->buckets = (dictbucket **)calloc (sizeof (dictbucket *), dict->num_buckets); + if (!dict->buckets) { + free (dict); + return NULL; + } + + dict->num_items = 0; + } + + return dict; +} + +void +p11_dict_free (p11_dict *dict) +{ + dictbucket *bucket; + p11_dictiter iter; + + if (!dict) + return; + + p11_dict_iterate (dict, &iter); + while ((bucket = next_entry (&iter)) != NULL) { + if (dict->key_destroy_func) + dict->key_destroy_func (bucket->key); + if (dict->value_destroy_func) + dict->value_destroy_func (bucket->value); + free (bucket); + } + + if (dict->buckets) + free (dict->buckets); + + free (dict); +} + +unsigned int +p11_dict_size (p11_dict *dict) +{ + return dict->num_items; +} + +unsigned int +p11_dict_str_hash (const void *string) +{ + const char *p = string; + unsigned int hash = *p; + + if (hash) + for (p += 1; *p != '\0'; p++) + hash = (hash << 5) - hash + *p; + + return hash; +} + +int +p11_dict_str_equal (const void *string_one, + const void *string_two) +{ + assert (string_one); + assert (string_two); + + return strcmp (string_one, string_two) == 0; +} + +unsigned int +p11_dict_ulongptr_hash (const void *to_ulong) +{ + assert (to_ulong); + return (unsigned int)*((unsigned long*)to_ulong); +} + +int +p11_dict_ulongptr_equal (const void *ulong_one, + const void *ulong_two) +{ + assert (ulong_one); + assert (ulong_two); + return *((unsigned long*)ulong_one) == *((unsigned long*)ulong_two); +} + +unsigned int +p11_dict_intptr_hash (const void *to_int) +{ + assert (to_int); + return (unsigned int)*((int*)to_int); +} + +int +p11_dict_intptr_equal (const void *int_one, + const void *int_two) +{ + assert (int_one); + assert (int_two); + return *((int*)int_one) == *((int*)int_two); +} + +unsigned int +p11_dict_direct_hash (const void *ptr) +{ + return (unsigned int)(size_t)ptr; +} + +int +p11_dict_direct_equal (const void *ptr_one, + const void *ptr_two) +{ + return ptr_one == ptr_two; +} diff --git a/common/dict.h b/common/dict.h new file mode 100644 index 0000000..1ba7185 --- /dev/null +++ b/common/dict.h @@ -0,0 +1,178 @@ +/* + * Copyright (c) 2004 Stefan Walter + * Copyright (c) 2011 Collabora Ltd. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * * Redistributions in binary form must reproduce the + * above copyright notice, this list of conditions and + * the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * * The names of contributors to this software may not be + * used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * Author: Stef Waler <stefw@collabora.co.uk> + */ + +#ifndef P11_DICT_H_ +#define P11_DICT_H_ + +#include <sys/types.h> + +/* + * ARGUMENT DOCUMENTATION + * + * dict: The dict + * key: Pointer to the key value + * val: Pointer to the value + * iter: A dict iterator + */ + + +/* ---------------------------------------------------------------------------------- + * TYPES + */ + +/* Abstract type for dicts. */ +typedef struct _p11_dict p11_dict; + +/* Type for scanning hash tables. */ +typedef struct _p11_dictiter { + p11_dict *dict; + struct _p11_dictbucket *next; + unsigned int index; +} p11_dictiter; + +typedef unsigned int (*p11_dict_hasher) (const void *data); + +typedef int (*p11_dict_equals) (const void *one, + const void *two); + +#ifndef P11_DESTROYER_DEFINED +#define P11_DESTROYER_DEFINED + +typedef void (*p11_destroyer) (void *data); + +#endif + +/* ----------------------------------------------------------------------------- + * MAIN + */ + +/* + * p11_dict_create : Create a hash table + * - returns an allocated hashtable + */ +p11_dict * p11_dict_new (p11_dict_hasher hasher, + p11_dict_equals equals, + p11_destroyer key_destroyer, + p11_destroyer value_destroyer); + +/* + * p11_dict_free : Free a hash table + */ +void p11_dict_free (p11_dict *dict); + +/* + * p11_dict_size: Number of values in hash table + * - returns the number of entries in hash table + */ +unsigned int p11_dict_size (p11_dict *dict); + +/* + * p11_dict_get: Retrieves a value from the hash table + * - returns the value of the entry + */ +void* p11_dict_get (p11_dict *dict, + const void *key); + +/* + * p11_dict_set: Set a value in the hash table + * - returns 1 if the entry was added properly + */ +int p11_dict_set (p11_dict *dict, + void *key, + void *value); + +/* + * p11_dict_remove: Remove a value from the hash table + * - returns 1 if the entry was found + */ +int p11_dict_remove (p11_dict *dict, + const void *key); + +/* + * p11_dict_steal: Remove a value from the hash table without calling + * destroy funcs + * - returns 1 if the entry was found + */ +int p11_dict_steal (p11_dict *dict, + const void *key, + void **stolen_key, + void **stolen_value); + +/* + * p11_dict_iterate: Start enumerating through the hash table + * - returns a hash iterator + */ +void p11_dict_iterate (p11_dict *dict, + p11_dictiter *iter); + +/* + * p11_dict_next: Enumerate through hash table + * - sets key and value to key and/or value + * - returns whether there was another entry + */ +int p11_dict_next (p11_dictiter *iter, + void **key, + void **value); + +/* + * p11_dict_clear: Clear all values from has htable. + */ +void p11_dict_clear (p11_dict *dict); + +/* ----------------------------------------------------------------------------- + * KEY FUNCTIONS + */ + +unsigned int p11_dict_str_hash (const void *string); + +int p11_dict_str_equal (const void *string_one, + const void *string_two); + +unsigned int p11_dict_ulongptr_hash (const void *to_ulong); + +int p11_dict_ulongptr_equal (const void *ulong_one, + const void *ulong_two); + +unsigned int p11_dict_intptr_hash (const void *to_int); + +int p11_dict_intptr_equal (const void *int_one, + const void *int_two); + +unsigned int p11_dict_direct_hash (const void *ptr); + +int p11_dict_direct_equal (const void *ptr_one, + const void *ptr_two); + +#endif /* __P11_DICT_H__ */ diff --git a/common/library.c b/common/library.c new file mode 100644 index 0000000..e8547b3 --- /dev/null +++ b/common/library.c @@ -0,0 +1,286 @@ +/* + * Copyright (c) 2011 Collabora Ltd + * Copyright (c) 2012 Stef Walter + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * * Redistributions in binary form must reproduce the + * above copyright notice, this list of conditions and + * the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * * The names of contributors to this software may not be + * used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * + * CONTRIBUTORS + * Stef Walter <stef@thewalter.net> + */ + +#include "config.h" + +#include "compat.h" +#define P11_DEBUG_FLAG P11_DEBUG_LIB +#include "debug.h" +#include "library.h" + +#include <assert.h> +#include <stdarg.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#define P11_MAX_MESSAGE 512 + +typedef struct { + char message[P11_MAX_MESSAGE]; +#ifdef OS_WIN32 + void *last_error; +#endif +} p11_local; + +static p11_local * _p11_library_get_thread_local (void); + +p11_mutex_t p11_library_mutex; + +#ifdef OS_UNIX +pthread_once_t p11_library_once; +#endif + +static int print_messages = 1; + +void +p11_message_store (const char* msg, + size_t length) +{ + p11_local *local; + + if (length > P11_MAX_MESSAGE - 1) + length = P11_MAX_MESSAGE - 1; + + local = _p11_library_get_thread_local (); + if (local != NULL) { + memcpy (local->message, msg, length); + local->message[length] = 0; + } +} + +void +p11_message (const char* msg, + ...) +{ + char buffer[P11_MAX_MESSAGE]; + va_list va; + size_t length; + + va_start (va, msg); + length = vsnprintf (buffer, P11_MAX_MESSAGE - 1, msg, va); + va_end (va); + + /* Was it truncated? */ + if (length > P11_MAX_MESSAGE - 1) + length = P11_MAX_MESSAGE - 1; + buffer[length] = 0; + + /* If printing is not disabled, just print out */ + if (print_messages) + fprintf (stderr, "p11-kit: %s\n", buffer); + + p11_debug_message (P11_DEBUG_LIB, "message: %s", buffer); + p11_message_store (buffer, length); +} + +void +p11_message_quiet (void) +{ + p11_lock (); + print_messages = 0; + p11_unlock (); +} + +const char* +p11_message_last (void) +{ + p11_local *local; + local = _p11_library_get_thread_local (); + return local && local->message[0] ? local->message : NULL; +} + +void +p11_message_clear (void) +{ + p11_local *local; + local = _p11_library_get_thread_local (); + if (local != NULL) + local->message[0] = 0; +} + +static void +uninit_common (void) +{ + p11_debug ("uninitializing library"); +} + +#ifdef OS_UNIX + +static pthread_key_t thread_local = 0; + +static p11_local * +_p11_library_get_thread_local (void) +{ + p11_local *local; + + p11_library_init_once (); + + local = pthread_getspecific (thread_local); + if (local == NULL) { + local = calloc (1, sizeof (p11_local)); + pthread_setspecific (thread_local, local); + } + + return local; +} + +#ifdef __GNUC__ +__attribute__((constructor)) +#endif +void +p11_library_init (void) +{ + p11_debug_init (); + p11_debug ("initializing library"); + p11_mutex_init (&p11_library_mutex); + pthread_key_create (&thread_local, free); +} + +#ifdef __GNUC__ +__attribute__((destructor)) +#endif +void +p11_library_uninit (void) +{ + uninit_common (); + + /* Some cleanup to pacify valgrind */ + free (pthread_getspecific (thread_local)); + pthread_setspecific (thread_local, NULL); + + pthread_key_delete (thread_local); + p11_mutex_uninit (&p11_library_mutex); +} + +#endif /* OS_UNIX */ + +#ifdef OS_WIN32 + +static DWORD thread_local = TLS_OUT_OF_INDEXES; + +BOOL WINAPI DllMain (HINSTANCE, DWORD, LPVOID); + +static p11_local * +_p11_library_get_thread_local (void) +{ + LPVOID data; + + if (thread_local == TLS_OUT_OF_INDEXES) + return NULL; + + data = TlsGetValue (thread_local); + if (data == NULL) { + data = LocalAlloc (LPTR, sizeof (p11_local)); + TlsSetValue (thread_local, data); + } + + return (p11_local *)data; +} + +void +p11_library_init (void) +{ + p11_debug_init (); + p11_debug ("initializing library"); + p11_mutex_init (&p11_library_mutex); + thread_local = TlsAlloc (); +} + +static void +free_tls_value (LPVOID data) +{ + p11_local *local = data; + if (local == NULL) + return; + if (local->last_error) + LocalFree (local->last_error); + LocalFree (data); +} + +void +p11_library_uninit (void) +{ + LPVOID data; + + uninit_common (); + + if (thread_local != TLS_OUT_OF_INDEXES) { + data = TlsGetValue (thread_local); + free_tls_value (data); + TlsFree (thread_local); + } + _p11_mutex_uninit (&p11_library_mutex); +} + + +BOOL WINAPI +DllMain (HINSTANCE instance, + DWORD reason, + LPVOID reserved) +{ + LPVOID data; + + switch (reason) { + case DLL_PROCESS_ATTACH: + p11_library_init (); + if (thread_local == TLS_OUT_OF_INDEXES) { + p11_debug ("couldn't setup tls"); + return FALSE; + } + break; + + case DLL_THREAD_DETACH: + if (thread_local != TLS_OUT_OF_INDEXES) { + p11_debug ("thread stopped, freeing tls"); + data = TlsGetValue (thread_local); + free_tls_value (data); + } + break; + + case DLL_PROCESS_DETACH: + p11_library_uninit (); + break; + + default: + break; + } + + return TRUE; +} + +#endif /* OS_WIN32 */ diff --git a/common/library.h b/common/library.h new file mode 100644 index 0000000..00e9c2c --- /dev/null +++ b/common/library.h @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2011 Collabora Ltd + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * * Redistributions in binary form must reproduce the + * above copyright notice, this list of conditions and + * the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * * The names of contributors to this software may not be + * used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * + * CONTRIBUTORS + * Stef Walter <stef@memberwebs.com> + */ + +#ifndef P11_LIBRARY_H_ +#define P11_LIBRARY_H_ + +#include "config.h" +#include "compat.h" + +#include <sys/types.h> + +extern p11_mutex_t p11_library_mutex; + +#define p11_lock() p11_mutex_lock (&p11_library_mutex); + +#define p11_unlock() p11_mutex_unlock (&p11_library_mutex); + +void p11_message (const char* msg, + ...) GNUC_PRINTF (1, 2); + +void p11_message_store (const char* msg, + size_t length); + +const char * p11_message_last (void); + +void p11_message_clear (void); + +void p11_message_quiet (void); + +#ifdef OS_WIN32 + +/* No implementation, because done by DllMain */ +#define p11_library_init_once() + +#else /* !OS_WIN32 */ +extern pthread_once_t p11_library_once; + +#define p11_library_init_once() \ + pthread_once (&p11_library_once, p11_library_init); + +#endif /* !OS_WIN32 */ + +void p11_library_init (void); + +void p11_library_uninit (void); + +#endif /* P11_LIBRARY_H_ */ diff --git a/common/tests/Makefile.am b/common/tests/Makefile.am new file mode 100644 index 0000000..11f2369 --- /dev/null +++ b/common/tests/Makefile.am @@ -0,0 +1,32 @@ + +include $(top_srcdir)/build/Makefile.tests + +NULL = + +COMMON = $(top_srcdir)/common + +INCLUDES = \ + -I$(top_srcdir) \ + -I$(srcdir)/.. \ + -I$(COMMON) \ + $(CUTEST_CFLAGS) + +LDADD = $(CUTEST_LIBS) + +CHECK_PROGS = \ + test-dict \ + test-array \ + $(NULL) + +test_dict_SOURCES = \ + test-dict.c \ + $(COMMON)/dict.c + +test_array_SOURCES = \ + test-array.c \ + $(COMMON)/array.c + +noinst_PROGRAMS = \ + $(CHECK_PROGS) + +TESTS = $(CHECK_PROGS:=$(EXEEXT)) diff --git a/common/tests/test-array.c b/common/tests/test-array.c new file mode 100644 index 0000000..a52f3b5 --- /dev/null +++ b/common/tests/test-array.c @@ -0,0 +1,194 @@ +/* + * Copyright (c) 2011, Collabora Ltd. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * * Redistributions in binary form must reproduce the + * above copyright notice, this list of conditions and + * the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * * The names of contributors to this software may not be + * used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * Author: Stef Walter <stefw@collabora.co.uk> + */ + +#include "config.h" +#include "CuTest.h" + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#include "array.h" + +static void +test_p11_array_create (CuTest *tc) +{ + p11_array *array; + + array = p11_array_new (NULL); + CuAssertPtrNotNull (tc, array); + p11_array_free (array); +} + +static void +test_p11_array_free_null (CuTest *tc) +{ + p11_array_free (NULL); +} + +static void +destroy_value (void *data) +{ + int *value = data; + *value = 2; +} + +static void +test_p11_array_free_destroys (CuTest *tc) +{ + p11_array *array; + int value = 0; + + array = p11_array_new (destroy_value); + CuAssertPtrNotNull (tc, array); + if (!p11_array_push (array, &value)) + CuFail (tc, "should not be reached"); + p11_array_free (array); + + CuAssertIntEquals (tc, 2, value); +} + +static void +test_p11_array_add (CuTest *tc) +{ + char *value = "VALUE"; + p11_array *array; + + array = p11_array_new (NULL); + if (!p11_array_push (array, value)) + CuFail (tc, "should not be reached"); + + CuAssertIntEquals (tc, 1, array->num); + CuAssertPtrEquals (tc, array->elem[0], value); + + p11_array_free (array); +} + +static void +test_p11_array_add_remove (CuTest *tc) +{ + char *value = "VALUE"; + p11_array *array; + + array = p11_array_new (NULL); + if (!p11_array_push (array, value)) + CuFail (tc, "should not be reached"); + + CuAssertIntEquals (tc, 1, array->num); + + CuAssertPtrEquals (tc, array->elem[0], value); + + p11_array_remove (array, 0); + + CuAssertIntEquals (tc, 0, array->num); + + p11_array_free (array); +} + +static void +test_p11_array_remove_destroys (CuTest *tc) +{ + p11_array *array; + int value = 0; + + array = p11_array_new (destroy_value); + if (!p11_array_push (array, &value)) + CuFail (tc, "should not be reached"); + + p11_array_remove (array, 0); + + CuAssertIntEquals (tc, 2, value); + + /* should not be destroyed again */ + value = 0; + + p11_array_free (array); + + CuAssertIntEquals (tc, 0, value); +} + +static void +test_p11_array_remove_and_count (CuTest *tc) +{ + p11_array *array; + int *value; + int i; + + array = p11_array_new (free); + + CuAssertIntEquals (tc, 0, array->num); + + for (i = 0; i < 20000; ++i) { + value = malloc (sizeof (int)); + *value = i; + if (!p11_array_push (array, value)) + CuFail (tc, "should not be reached"); + CuAssertIntEquals (tc, i + 1, array->num); + } + + for (i = 10; i < 20000; ++i) { + p11_array_remove (array, 10); + CuAssertIntEquals (tc, 20010 - (i + 1), array->num); + } + + CuAssertIntEquals (tc, 10, array->num); + + p11_array_free (array); +} + +int +main (void) +{ + CuString *output = CuStringNew (); + CuSuite* suite = CuSuiteNew (); + int ret; + + SUITE_ADD_TEST (suite, test_p11_array_create); + SUITE_ADD_TEST (suite, test_p11_array_add); + SUITE_ADD_TEST (suite, test_p11_array_add_remove); + SUITE_ADD_TEST (suite, test_p11_array_remove_destroys); + SUITE_ADD_TEST (suite, test_p11_array_remove_and_count); + SUITE_ADD_TEST (suite, test_p11_array_free_null); + SUITE_ADD_TEST (suite, test_p11_array_free_destroys); + + CuSuiteRun (suite); + CuSuiteSummary (suite, output); + CuSuiteDetails (suite, output); + printf ("%s\n", output->buffer); + ret = suite->failCount; + CuSuiteDelete (suite); + CuStringDelete (output); + + return ret; +} diff --git a/common/tests/test-dict.c b/common/tests/test-dict.c new file mode 100644 index 0000000..3af3daa --- /dev/null +++ b/common/tests/test-dict.c @@ -0,0 +1,465 @@ +/* + * Copyright (c) 2011, Collabora Ltd. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * * Redistributions in binary form must reproduce the + * above copyright notice, this list of conditions and + * the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * * The names of contributors to this software may not be + * used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * Author: Stef Walter <stefw@collabora.co.uk> + */ + +#include "config.h" +#include "CuTest.h" + +#include <assert.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#include "dict.h" + +static void +test_create (CuTest *tc) +{ + p11_dict *map; + + map = p11_dict_new (p11_dict_direct_hash, p11_dict_direct_equal, NULL, NULL); + CuAssertPtrNotNull (tc, map); + p11_dict_free (map); +} + +static void +test_free_null (CuTest *tc) +{ + p11_dict_free (NULL); +} + +typedef struct { + int value; + int freed; +} Key; + +static unsigned int +key_hash (const void *ptr) +{ + const Key *k = ptr; + assert (!k->freed); + return p11_dict_intptr_hash (&k->value); +} + +static int +key_equal (const void *one, + const void *two) +{ + const Key *k1 = one; + const Key *k2 = two; + assert (!k1->freed); + assert (!k2->freed); + return p11_dict_intptr_equal (&k1->value, &k2->value); +} + +static void +key_destroy (void *data) +{ + Key *k = data; + assert (!k->freed); + k->freed = 1; +} + +static void +value_destroy (void *data) +{ + int *value = data; + *value = 2; +} + +static void +test_free_destroys (CuTest *tc) +{ + p11_dict *map; + Key key = { 8, 0 }; + int value = 0; + + map = p11_dict_new (key_hash, key_equal, key_destroy, value_destroy); + CuAssertPtrNotNull (tc, map); + if (!p11_dict_set (map, &key, &value)) + CuFail (tc, "should not be reached"); + p11_dict_free (map); + + CuAssertIntEquals (tc, 1, key.freed); + CuAssertIntEquals (tc, 2, value); +} + +static void +test_iterate (CuTest *tc) +{ + p11_dict *map; + p11_dictiter iter; + int key = 1; + int value = 2; + void *pkey; + void *pvalue; + int ret; + + map = p11_dict_new (p11_dict_direct_hash, p11_dict_direct_equal, NULL, NULL); + CuAssertPtrNotNull (tc, map); + if (!p11_dict_set (map, &key, &value)) + CuFail (tc, "should not be reached"); + + p11_dict_iterate (map, &iter); + + ret = p11_dict_next (&iter, &pkey, &pvalue); + CuAssertIntEquals (tc, 1, ret); + CuAssertPtrEquals (tc, pkey, &key); + CuAssertPtrEquals (tc, pvalue, &value); + + ret = p11_dict_next (&iter, &pkey, &pvalue); + CuAssertIntEquals (tc, 0, ret); + + p11_dict_free (map); +} + +static void +test_set_get (CuTest *tc) +{ + char *key = "KEY"; + char *value = "VALUE"; + char *check; + p11_dict *map; + + map = p11_dict_new (p11_dict_str_hash, p11_dict_str_equal, NULL, NULL); + p11_dict_set (map, key, value); + check = p11_dict_get (map, key); + CuAssertPtrEquals (tc, check, value); + + p11_dict_free (map); +} + +static void +test_set_get_remove (CuTest *tc) +{ + char *key = "KEY"; + char *value = "VALUE"; + char *check; + p11_dict *map; + int ret; + + map = p11_dict_new (p11_dict_str_hash, p11_dict_str_equal, NULL, NULL); + + if (!p11_dict_set (map, key, value)) + CuFail (tc, "should not be reached"); + + check = p11_dict_get (map, key); + CuAssertPtrEquals (tc, check, value); + + ret = p11_dict_remove (map, key); + CuAssertIntEquals (tc, ret, 1); + ret = p11_dict_remove (map, key); + CuAssertIntEquals (tc, ret, 0); + + check = p11_dict_get (map, key); + CuAssert (tc, "should be null", check == NULL); + + p11_dict_free (map); +} + +static void +test_set_clear (CuTest *tc) +{ + char *key = "KEY"; + char *value = "VALUE"; + char *check; + p11_dict *map; + + map = p11_dict_new (p11_dict_direct_hash, p11_dict_direct_equal, NULL, NULL); + + if (!p11_dict_set (map, key, value)) + CuFail (tc, "should not be reached"); + + p11_dict_clear (map); + + check = p11_dict_get (map, key); + CuAssert (tc, "should be null", check == NULL); + + p11_dict_free (map); +} + +static void +test_remove_destroys (CuTest *tc) +{ + p11_dict *map; + Key key = { 8, 0 }; + int value = 0; + int ret; + + map = p11_dict_new (key_hash, key_equal, key_destroy, value_destroy); + CuAssertPtrNotNull (tc, map); + if (!p11_dict_set (map, &key, &value)) + CuFail (tc, "should not be reached"); + + ret = p11_dict_remove (map, &key); + CuAssertIntEquals (tc, ret, 1); + CuAssertIntEquals (tc, 1, key.freed); + CuAssertIntEquals (tc, 2, value); + + /* should not be destroyed again */ + key.freed = 0; + value = 0; + + ret = p11_dict_remove (map, &key); + CuAssertIntEquals (tc, ret, 0); + CuAssertIntEquals (tc, 0, key.freed); + CuAssertIntEquals (tc, 0, value); + + /* should not be destroyed again */ + key.freed = 0; + value = 0; + + p11_dict_free (map); + + CuAssertIntEquals (tc, 0, key.freed); + CuAssertIntEquals (tc, 0, value); +} + +static void +test_set_destroys (CuTest *tc) +{ + p11_dict *map; + Key key = { 8, 0 }; + Key key2 = { 8, 0 }; + int value, value2; + int ret; + + map = p11_dict_new (key_hash, key_equal, key_destroy, value_destroy); + CuAssertPtrNotNull (tc, map); + if (!p11_dict_set (map, &key, &value)) + CuFail (tc, "should not be reached"); + + key.freed = key2.freed = value = value2 = 0; + + /* Setting same key and value, should not be destroyed */ + ret = p11_dict_set (map, &key, &value); + CuAssertIntEquals (tc, ret, 1); + CuAssertIntEquals (tc, 0, key.freed); + CuAssertIntEquals (tc, 0, key2.freed); + CuAssertIntEquals (tc, 0, value); + CuAssertIntEquals (tc, 0, value2); + + key.freed = key2.freed = value = value2 = 0; + + /* Setting a new key same value, key should be destroyed */ + ret = p11_dict_set (map, &key2, &value); + CuAssertIntEquals (tc, ret, 1); + CuAssertIntEquals (tc, 1, key.freed); + CuAssertIntEquals (tc, 0, key2.freed); + CuAssertIntEquals (tc, 0, value); + CuAssertIntEquals (tc, 0, value2); + + key.freed = key2.freed = value = value2 = 0; + + /* Setting same key, new value, value should be destroyed */ + ret = p11_dict_set (map, &key2, &value2); + CuAssertIntEquals (tc, ret, 1); + CuAssertIntEquals (tc, 0, key.freed); + CuAssertIntEquals (tc, 0, key2.freed); + CuAssertIntEquals (tc, 2, value); + CuAssertIntEquals (tc, 0, value2); + + key.freed = key2.freed = value = value2 = 0; + + /* Setting new key new value, both should be destroyed */ + ret = p11_dict_set (map, &key, &value); + CuAssertIntEquals (tc, ret, 1); + CuAssertIntEquals (tc, 0, key.freed); + CuAssertIntEquals (tc, 1, key2.freed); + CuAssertIntEquals (tc, 0, value); + CuAssertIntEquals (tc, 2, value2); + + key.freed = key2.freed = value = value2 = 0; + + p11_dict_free (map); + CuAssertIntEquals (tc, 1, key.freed); + CuAssertIntEquals (tc, 2, value); + CuAssertIntEquals (tc, 0, key2.freed); + CuAssertIntEquals (tc, 0, value2); +} + + +static void +test_clear_destroys (CuTest *tc) +{ + p11_dict *map; + Key key = { 18, 0 }; + int value = 0; + + map = p11_dict_new (key_hash, key_equal, key_destroy, value_destroy); + CuAssertPtrNotNull (tc, map); + if (!p11_dict_set (map, &key, &value)) + CuFail (tc, "should not be reached"); + + p11_dict_clear (map); + CuAssertIntEquals (tc, 1, key.freed); + CuAssertIntEquals (tc, 2, value); + + /* should not be destroyed again */ + key.freed = 0; + value = 0; + + p11_dict_clear (map); + CuAssertIntEquals (tc, 0, key.freed); + CuAssertIntEquals (tc, 0, value); + + /* should not be destroyed again */ + key.freed = 0; + value = 0; + + p11_dict_free (map); + + CuAssertIntEquals (tc, 0, key.freed); + CuAssertIntEquals (tc, 0, value); +} + +static unsigned int +test_hash_intptr_with_collisions (const void *data) +{ + /* lots and lots of collisions, only returns 100 values */ + return (unsigned int)(*((int*)data) % 100); +} + +static void +test_hash_add_check_lots_and_collisions (CuTest *tc) +{ + p11_dict *map; + int *value; + int i; + + map = p11_dict_new (test_hash_intptr_with_collisions, + p11_dict_intptr_equal, NULL, free); + + for (i = 0; i < 20000; ++i) { + value = malloc (sizeof (int)); + *value = i; + if (!p11_dict_set (map, value, value)) + CuFail (tc, "should not be reached"); + } + + for (i = 0; i < 20000; ++i) { + value = p11_dict_get (map, &i); + CuAssertPtrNotNull (tc, value); + CuAssertIntEquals (tc, i, *value); + } + + p11_dict_free (map); +} + +static void +test_hash_count (CuTest *tc) +{ + p11_dict *map; + int *value; + int i, ret; + + map = p11_dict_new (p11_dict_intptr_hash, p11_dict_intptr_equal, NULL, free); + + CuAssertIntEquals (tc, 0, p11_dict_size (map)); + + for (i = 0; i < 20000; ++i) { + value = malloc (sizeof (int)); + *value = i; + if (!p11_dict_set (map, value, value)) + CuFail (tc, "should not be reached"); + CuAssertIntEquals (tc, i + 1, p11_dict_size (map)); + } + + for (i = 0; i < 20000; ++i) { + ret = p11_dict_remove (map, &i); + CuAssertIntEquals (tc, 1, ret); + CuAssertIntEquals (tc, 20000 - (i + 1), p11_dict_size (map)); + } + + p11_dict_clear (map); + CuAssertIntEquals (tc, 0, p11_dict_size (map)); + + p11_dict_free (map); +} + +static void +test_hash_ulongptr (CuTest *tc) +{ + p11_dict *map; + unsigned long *value; + unsigned long i; + + map = p11_dict_new (p11_dict_ulongptr_hash, p11_dict_ulongptr_equal, NULL, free); + + for (i = 0; i < 20000; ++i) { + value = malloc (sizeof (unsigned long)); + *value = i; + if (!p11_dict_set (map, value, value)) + CuFail (tc, "should not be reached"); + } + + for (i = 0; i < 20000; ++i) { + value = p11_dict_get (map, &i); + CuAssertPtrNotNull (tc, value); + CuAssertIntEquals (tc, i, *value); + } + + p11_dict_free (map); +} + +int +main (void) +{ + CuString *output = CuStringNew (); + CuSuite* suite = CuSuiteNew (); + int ret; + + SUITE_ADD_TEST (suite, test_create); + SUITE_ADD_TEST (suite, test_set_get); + SUITE_ADD_TEST (suite, test_set_get_remove); + SUITE_ADD_TEST (suite, test_remove_destroys); + SUITE_ADD_TEST (suite, test_set_clear); + SUITE_ADD_TEST (suite, test_set_destroys); + SUITE_ADD_TEST (suite, test_clear_destroys); + SUITE_ADD_TEST (suite, test_free_null); + SUITE_ADD_TEST (suite, test_free_destroys); + SUITE_ADD_TEST (suite, test_iterate); + SUITE_ADD_TEST (suite, test_hash_add_check_lots_and_collisions); + SUITE_ADD_TEST (suite, test_hash_count); + SUITE_ADD_TEST (suite, test_hash_ulongptr); + + CuSuiteRun (suite); + CuSuiteSummary (suite, output); + CuSuiteDetails (suite, output); + printf ("%s\n", output->buffer); + ret = suite->failCount; + CuSuiteDelete (suite); + CuStringDelete (output); + + return ret; +} |