diff options
Diffstat (limited to 'common/refarray')
-rw-r--r-- | common/refarray/Makefile.am | 36 | ||||
-rw-r--r-- | common/refarray/configure.ac | 30 | ||||
-rw-r--r-- | common/refarray/m4/.dir | 0 | ||||
-rw-r--r-- | common/refarray/ref_array.c | 294 | ||||
-rw-r--r-- | common/refarray/ref_array.h | 84 | ||||
-rw-r--r-- | common/refarray/ref_array.pc.in | 11 | ||||
-rw-r--r-- | common/refarray/ref_array_ut.c | 343 |
7 files changed, 798 insertions, 0 deletions
diff --git a/common/refarray/Makefile.am b/common/refarray/Makefile.am new file mode 100644 index 00000000..573173ac --- /dev/null +++ b/common/refarray/Makefile.am @@ -0,0 +1,36 @@ +TRACE_LEVEL=@TRACE_VAR@ + +topdir=$(srcdir)/.. + +AM_CFLAGS = + +if HAVE_GCC + AM_CFLAGS += \ + -Wall -Wshadow -Wstrict-prototypes -Wpointer-arith -Wcast-qual \ + -Wcast-align -Wwrite-strings +endif + +AM_CPPFLAGS = -I$(topdir)/trace -I$(topdir)/elapi/refarray $(TRACE_LEVEL) + +ACLOCAL_AMFLAGS = -I m4 + +# Set up the pkg-config file +pkgconfigdir = $(libdir)/pkgconfig +dist_noinst_DATA = \ + ref_array.pc \ + m4 + +# Build library +noinst_LTLIBRARIES = libref_array.la +libref_array_la_SOURCES = \ + ref_array.c \ + ref_array.h + +# Build unit test +check_PROGRAMS = ref_array_ut +ref_array_ut_SOURCES = ref_array_ut.c +ref_array_ut_LDADD = libref_array.la + +TESTS = ref_array_ut + +tests: all $(check_PROGRAMS) diff --git a/common/refarray/configure.ac b/common/refarray/configure.ac new file mode 100644 index 00000000..b220c521 --- /dev/null +++ b/common/refarray/configure.ac @@ -0,0 +1,30 @@ +m4_include([../../version.m4]) +AC_INIT([sssd_libs], + VERSION_NUMBER, + [sssd-devel@lists.fedorahosted.org]) +AC_CONFIG_SRCDIR([ref_array.c]) +AC_CONFIG_AUX_DIR([build]) +AM_INIT_AUTOMAKE([-Wall -Werror foreign]) +AC_PROG_CC +AC_PROG_LIBTOOL +AC_CONFIG_MACRO_DIR([m4]) +AC_PROG_INSTALL + +AM_CONDITIONAL([HAVE_GCC], [test "$ac_cv_prog_gcc" = yes]) + +m4_pattern_allow([AM_SILENT_RULES]) +AM_SILENT_RULES + +AC_CONFIG_HEADERS([config.h]) + +# Enable trace build +AC_ARG_ENABLE([trace], + [AS_HELP_STRING([--enable-trace[=LEVEL]],[build with low level tracing enabled])], + [trace_level="$enableval"], + [trace_level="0"]) + +AS_IF([test ["$trace_level" -gt "0"] -a ["$trace_level" -lt "8"] ],[AC_SUBST([TRACE_VAR],["-DTRACE_LEVEL=$trace_level"])]) + +AC_CONFIG_FILES([Makefile ref_array.pc]) + +AC_OUTPUT diff --git a/common/refarray/m4/.dir b/common/refarray/m4/.dir new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/common/refarray/m4/.dir diff --git a/common/refarray/ref_array.c b/common/refarray/ref_array.c new file mode 100644 index 00000000..d33a16b4 --- /dev/null +++ b/common/refarray/ref_array.c @@ -0,0 +1,294 @@ +/* + REF ARRAY + + Implementation of the dynamic array with reference count. + + Copyright (C) Dmitri Pal <dpal@redhat.com> 2009 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#define _GNU_SOURCE +#include <errno.h> /* for errors */ +#include <stdint.h> +#include <malloc.h> +#include <string.h> +#include <stdio.h> + +#include "ref_array.h" +#include "config.h" +#include "trace.h" + +/* The structure used in referenced array */ +struct ref_array { + void *storage; /* The storage buffer */ + size_t elsize; /* Size of one element in the buffer */ + uint32_t size; /* Size of the storage in items */ + uint32_t grow_by; /* What increment use to reallocate memory */ + uint32_t len; /* Number of the elements in the array */ + uint32_t refcount; /* Reference count */ + ref_array_fn cb; /* Cleanup callback */ + void *cb_data; /* Caller's callback data */ +}; + +/****************************************************/ +/* INTERNAL FUNCTIONS */ +/****************************************************/ +static int ref_array_grow(struct ref_array *ra) +{ + int error = EOK; + void *newbuf = NULL; + + TRACE_FLOW_STRING("ref_array_grow", "Entry"); + + TRACE_INFO_NUMBER("Current length: ", ra->len); + TRACE_INFO_NUMBER("Current size: ", ra->size); + + /* Grow buffer if needed */ + newbuf = realloc(ra->storage, (ra->size + ra->grow_by) * ra->elsize); + if (newbuf == NULL) { + TRACE_ERROR_NUMBER("Failed to allocate memory.", ENOMEM); + return ENOMEM; + } + + ra->storage = newbuf; + ra->size += ra->grow_by; + + TRACE_INFO_NUMBER("Final size: ", ra->size); + TRACE_FLOW_NUMBER("elapi_grow_data. Exit. Returning", error); + return error; + +} + + +/****************************************************/ +/* PUBLIC FUNCTIONS */ +/****************************************************/ + +/* Create referenced array */ +int ref_array_create(struct ref_array **ra, + size_t elemsz, + uint32_t grow_by, + ref_array_fn cb, + void *data) +{ + struct ref_array *new_ra = NULL; + + TRACE_FLOW_STRING("ref_array_create", "Entry"); + + if (!ra) { + TRACE_ERROR_NUMBER("Uninitialized argument.", EINVAL); + return EINVAL; + } + + if ((!elemsz) || (!grow_by)) { + TRACE_ERROR_NUMBER("Invalid argument.", EINVAL); + return EINVAL; + } + + new_ra = (struct ref_array *)malloc(sizeof(struct ref_array)); + + if (!new_ra) { + TRACE_ERROR_NUMBER("Failed to allocate memory.", ENOMEM); + return ENOMEM; + } + + new_ra->storage = NULL; + new_ra->elsize = elemsz; + new_ra->size = 0; + new_ra->grow_by = grow_by; + new_ra->len = 0; + new_ra->refcount = 1; + new_ra->cb = cb; + new_ra->cb_data = data; + + *ra = new_ra; + + TRACE_FLOW_STRING("ref_array_create", "Exit"); + return EOK; +} + +/* Get new reference to an array */ +struct ref_array *ref_array_getref(struct ref_array *ra) +{ + TRACE_FLOW_STRING("ref_array_getref", "Entry"); + + /* Check if array is not NULL */ + if (ra) { + TRACE_INFO_NUMBER("Increasing reference count. Current: ", ra->refcount); + /* Increase reference count */ + ra->refcount++; + TRACE_INFO_NUMBER("Increased reference count. New: ", ra->refcount); + + } + else { + TRACE_ERROR_STRING("Uninitialized array.", "Returning NULL"); + } + + TRACE_FLOW_STRING("ref_array_getref", "Exit"); + return ra; +} + +/* Delete the array */ +void ref_array_destroy(struct ref_array *ra) +{ + int idx; + + TRACE_FLOW_STRING("ref_array_destroy", "Entry"); + + /* Check if array is not NULL */ + if (!ra) { + TRACE_ERROR_STRING("Uninitialized array.", "Coding error???"); + return; + } + + TRACE_INFO_NUMBER("Current reference count: ", ra->refcount); + if (ra->refcount) { + /* Decrease reference count */ + ra->refcount--; + if (ra->refcount == 0) { + TRACE_INFO_NUMBER("It is time to delete array. Count:", ra->refcount); + if (ra->cb) { + for (idx = 0; idx < ra->len; idx++) { + ra->cb((unsigned char *)(ra->storage) + idx * ra->elsize, + REF_ARRAY_DESTROY, ra->cb_data); + } + } + free(ra->storage); + free(ra); + } + } + else { + /* Should never be here... + * This can happen if the caller by mistake would try to + * destroy the object from within the callback. Brrr.... + */ + TRACE_ERROR_STRING("Reference count is 0.", "Coding error???"); + } + + TRACE_FLOW_STRING("ref_array_destroy", "Exit"); +} + +/* Add new element to the array */ +int ref_array_append(struct ref_array *ra, void *element) +{ + int error = EOK; + + TRACE_FLOW_STRING("ref_array_append", "Entry"); + if ((!ra) || (!element)) { + TRACE_ERROR_NUMBER("Uninitialized argument.", EINVAL); + return EINVAL; + } + + /* Do we have enough room for a new element? */ + if (ra->size == ra->len) { + error = ref_array_grow(ra); + if (error) { + TRACE_ERROR_NUMBER("Failed to grow array.", error); + return EINVAL; + } + } + + /* Copy element */ + memcpy((unsigned char *)(ra->storage) + ra->len * ra->elsize, + element, + ra->elsize); + + ra->len++; + + TRACE_FLOW_STRING("ref_array_append", "Exit"); + return error; +} + +/* Get element */ +void *ref_array_get(struct ref_array *ra, uint32_t idx, void *acptr) +{ + TRACE_FLOW_STRING("ref_array_get", "Entry"); + + if (!ra) { + TRACE_ERROR_STRING("Uninitialized argument.", ""); + return NULL; + } + + if (idx >= ra->len) { + TRACE_ERROR_NUMBER("Invalid idx.", idx); + return NULL; + } + + TRACE_INFO_NUMBER("Index: ", idx); + + if (acptr) { + + TRACE_INFO_STRING("Copying data.", ""); + memcpy(acptr, + (unsigned char *)(ra->storage) + idx * ra->elsize, + ra->elsize); + + } + + TRACE_FLOW_STRING("ref_array_get returning internal storage", "Exit"); + return (unsigned char *)(ra->storage) + idx * ra->elsize; +} + + +/* Get length */ +int ref_array_getlen(struct ref_array *ra, uint32_t *len) +{ + TRACE_FLOW_STRING("ref_array_getlen", "Entry"); + + if ((!ra) || (!len)) { + TRACE_ERROR_STRING("Uninitialized argument.", ""); + return EINVAL; + } + + *len = ra->len; + + TRACE_FLOW_STRING("ref_array_getlen", "Exit"); + return EOK; +} + +/* Alternative function to get length */ +uint32_t ref_array_len(struct ref_array *ra) +{ + TRACE_FLOW_STRING("ref_array_len", "Entry"); + + if (!ra) { + TRACE_ERROR_STRING("Uninitialized argument.", ""); + errno = EINVAL; + return 0; + } + + TRACE_FLOW_STRING("ref_array_len", "Exit"); + return ra->len; +} + + +/* Debug function */ +void ref_array_debug(struct ref_array *ra) +{ + int i,j; + + printf("\nARRAY DUMP START\n"); + printf("Length = %u\n", ra->len); + printf("Size = %u\n", ra->size); + printf("Element = %u\n", (unsigned int)(ra->elsize)); + printf("Grow by = %u\n", ra->grow_by); + printf("Count = %u\n", ra->refcount); + printf("ARRAY:\n"); + for (i = 0; i < ra->len; i++) { + for (j = 0; j < ra->elsize; j++) { + printf("%x", *((unsigned char *)(ra->storage) + i * ra->elsize + j)); + } + printf("\n%s\n", *((char **)((unsigned char *)(ra->storage) + i * ra->elsize))); + } + printf("\nARRAY DUMP END\n\n"); +} diff --git a/common/refarray/ref_array.h b/common/refarray/ref_array.h new file mode 100644 index 00000000..42112907 --- /dev/null +++ b/common/refarray/ref_array.h @@ -0,0 +1,84 @@ +/* + REF ARRAY + + Header file for of the dynamic array with reference count. + + Copyright (C) Dmitri Pal <dpal@redhat.com> 2009 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef REF_ARRAY_H +#define REF_ARRAY_H + +#include <stdint.h> +#include <stdlib.h> + +struct ref_array; + +#ifndef EOK +#define EOK 0 +#endif + +/*************************************/ +/* Interface to the referenced array */ +/*************************************/ + +typedef enum +{ + REF_ARRAY_DESTROY, + REF_ARRAY_DELETE, +} ref_array_del_enum; + +/* Callback that can be provided by caller + * to free data when the storage is actually destroyed + */ +typedef void (*ref_array_fn)(void *elem, + ref_array_del_enum type, + void *data); + + +/* Create referenced array */ +int ref_array_create(struct ref_array **ra, + size_t elem, + uint32_t grow_by, + ref_array_fn cb, + void *data); + +/* Get new reference to an array */ +struct ref_array *ref_array_getref(struct ref_array *ra); + +/* Delete the array */ +void ref_array_destroy(struct ref_array *ra); + +/* Add new element to the array */ +int ref_array_append(struct ref_array *ra, void *element); + +/* Get element */ +void *ref_array_get(struct ref_array *ra, uint32_t idx, void *acptr); + +/* Get array length */ +int ref_array_getlen(struct ref_array *ra, uint32_t *len); + +/* Alternative function to get length. + * Returns 0 if the array is invalid + */ +uint32_t ref_array_len(struct ref_array *ra); + + +/* In future in might make sense to add entry points + * to insert and delete elements from the array. + * Current use cases do not require this kind of + * functionality so it is left out of the implementation + */ + +#endif diff --git a/common/refarray/ref_array.pc.in b/common/refarray/ref_array.pc.in new file mode 100644 index 00000000..1fc06f71 --- /dev/null +++ b/common/refarray/ref_array.pc.in @@ -0,0 +1,11 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: @PACKAGE_NAME@ +Description: Reference library +Version: @PACKAGE_VERSION@ +Libs: -L${libdir} -lref_array +Cflags: -I${includedir} +URL: http://fedorahosted.org/sssd/ diff --git a/common/refarray/ref_array_ut.c b/common/refarray/ref_array_ut.c new file mode 100644 index 00000000..c85b6b26 --- /dev/null +++ b/common/refarray/ref_array_ut.c @@ -0,0 +1,343 @@ +/* + REF ARRAY + + Implementation of the dynamic array with reference count. + + Copyright (C) Dmitri Pal <dpal@redhat.com> 2009 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#define _GNU_SOURCE +#include <errno.h> /* for errors */ +#include <stdint.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +#include "ref_array.h" +#include "config.h" +#define TRACE_HOME +#include "trace.h" + +int verbose = 0; + +#define RAOUT(foo) \ + do { \ + if (verbose) foo; \ + } while(0) + +extern void ref_array_debug(struct ref_array *ra); + +typedef int (*test_fn)(void); + +/* Basic test */ +int ref_array_basic_test(void) +{ + const char *line1 = "line1"; + const char *line2 = "line2"; + const char *line3 = "line3"; + const char *line4 = "line4"; + const char *line5 = "line5"; + const char *line6 = "line6"; + uint32_t i; + struct ref_array *ra; + struct ref_array *ra2; + int error = EOK; + uint32_t len = 0; + uint32_t other_len = 0; + char *ret; + char *elem; + void *ptr; + + error = ref_array_create(&ra, sizeof(char *), 1, NULL, NULL); + if (error) { + printf("Failed to create array %d\n", error); + return error; + } + + RAOUT(ref_array_debug(ra)); + + error = ref_array_append(ra, &line1); + if (error) { + ref_array_destroy(ra); + printf("Failed to append to array line 1 %d\n", error); + return error; + } + + RAOUT(ref_array_debug(ra)); + + error = ref_array_append(ra, &line2); + if (error) { + ref_array_destroy(ra); + printf("Failed to append to array line 2 %d\n", error); + return error; + } + + RAOUT(ref_array_debug(ra)); + + error = ref_array_append(ra, &line3); + if (error) { + ref_array_destroy(ra); + printf("Failed to append to array line 3 %d\n", error); + return error; + } + + RAOUT(ref_array_debug(ra)); + + error = ref_array_append(ra, &line4); + if (error) { + ref_array_destroy(ra); + printf("Failed to append to array line 4 %d\n", error); + return error; + } + + RAOUT(ref_array_debug(ra)); + + error = ref_array_append(ra, &line5); + if (error) { + ref_array_destroy(ra); + printf("Failed to append to array line 5 %d\n", error); + return error; + } + + RAOUT(ref_array_debug(ra)); + + error = ref_array_append(ra, &line6); + if (error) { + ref_array_destroy(ra); + printf("Failed to append to array line 6 %d\n", error); + return error; + } + + RAOUT(ref_array_debug(ra)); + + RAOUT(printf("\n\nTest 1 - Printing lines.\n\n")); + + error = ref_array_getlen(ra, &other_len); + if (error) { + ref_array_destroy(ra); + printf("Failed to get length %d\n", error); + return error; + } + + len = ref_array_len(ra); + + if (len != other_len) { + ref_array_destroy(ra); + printf("Lengths do not match:\n"); + printf("Len : %d\n", len); + printf("Get Len: %d\n", other_len); + return EFAULT; + } + + for (i = 0; i < len; i++) { + ref_array_get(ra, i, &ret); + RAOUT(printf("%s\n", ret)); + } + + RAOUT(printf("\n\nTest 2 - Creating reference and then printing lines.\n\n")); + + ra2 = ref_array_getref(ra); + ref_array_destroy(ra); + + for (i = 0; i < len; i++) { + ret = *((char **)ref_array_get(ra2, i, NULL)); + RAOUT(printf("%s\n", ret)); + } + + RAOUT(printf("\n\nTest 3 - Get elements with copying.\n\n")); + + for (i = 0; i < len; i++) { + ref_array_get(ra2, i, &ret); + RAOUT(printf("%s\n", ret)); + } + + RAOUT(printf("\n\nTest 4a - Get elements with copying and assignment.\n\n")); + + /* This is a bad practice to use one variable + * as a parameter and as an acceptor for the return value. + * See next example for a better way to do it. + */ + for (i = 0; i < len; i++) { + ret = *((char **)ref_array_get(ra2, i, &ret)); + RAOUT(printf("%s\n", ret)); + } + + RAOUT(printf("\n\nTest 4b - Get elements with copying and assignment.\n\n")); + + for (i = 0; i < len; i++) { + ret = *((char **)ref_array_get(ra2, i, &elem)); + RAOUT(printf("%s\n", ret)); + RAOUT(printf("%s\n", elem)); + if (strcmp(ret, elem) != 0) { + ref_array_destroy(ra2); + printf("\nRetrieved strings were expected to be same,\n"); + printf("but they are not:\n"); + printf("By pointer:[%s]\nAs element:[%s]\n", ret, elem); + return EFAULT; + } + } + + RAOUT(printf("\n\nTest 5 - While loop up.\n\n")); + + i = 0; + for (;;) { + ptr = ref_array_get(ra2, i, &ret); + if (ptr) { + RAOUT(printf("%s\n", ret)); + i++; + } + else break; + } + + RAOUT(printf("\n\nTest 6 - While loop down.\n\n")); + + i = len - 1; + for (;;) { + ptr = ref_array_get(ra2, i, &ret); + if (ptr) { + RAOUT(printf("%s\n", ret)); + i--; + } + else break; + } + + RAOUT(printf("\n\nDone!!!\n\n")); + + ref_array_destroy(ra2); + return EOK; +} + +void array_cleanup(void *elem, + ref_array_del_enum type, + void *data) +{ + RAOUT(printf("%s%s\n", (char *)data, *((char **)elem))); + free(*((char **)elem)); +} + +/* Free test */ +int ref_array_free_test(void) +{ + const char *line1 = "line1"; + const char *line2 = "line2"; + const char *line3 = "line3"; + const char *line4 = "line4"; + char text[] = "Deleting: "; + char *str; + uint32_t i; + struct ref_array *ra; + int error = EOK; + char *ret; + void *ptr; + + error = ref_array_create(&ra, sizeof(char *), 1, array_cleanup, (char *)text); + if (error) { + printf("Failed to create array %d\n", error); + return error; + } + + RAOUT(ref_array_debug(ra)); + + str = strdup(line1); + + error = ref_array_append(ra, &str); + if (error) { + ref_array_destroy(ra); + printf("Failed to append to array line 1 %d\n", error); + return error; + } + + RAOUT(ref_array_debug(ra)); + + str = strdup(line2); + + error = ref_array_append(ra, &str); + if (error) { + ref_array_destroy(ra); + printf("Failed to append to array line 2 %d\n", error); + return error; + } + + RAOUT(ref_array_debug(ra)); + + str = strdup(line3); + + error = ref_array_append(ra, &str); + if (error) { + ref_array_destroy(ra); + printf("Failed to append to array line 3 %d\n", error); + return error; + } + + RAOUT(ref_array_debug(ra)); + + str = strdup(line4); + + error = ref_array_append(ra, &str); + if (error) { + ref_array_destroy(ra); + printf("Failed to append to array line 4 %d\n", error); + return error; + } + + RAOUT(ref_array_debug(ra)); + + i = 0; + for (;;) { + ptr = ref_array_get(ra, i, &ret); + if (ptr) { + RAOUT(printf("%s\n", ret)); + i++; + } + else break; + } + + RAOUT(printf("\n\nDone!!!\n\n")); + + ref_array_destroy(ra); + return EOK; +} + + + +/* Main function of the unit test */ +int main(int argc, char *argv[]) +{ + int error = 0; + test_fn tests[] = { ref_array_basic_test, + ref_array_free_test, + NULL }; + test_fn t; + int i = 0; + char *var; + + if ((argc > 1) && (strcmp(argv[1], "-v") == 0)) verbose = 1; + else { + var = getenv("COMMON_TEST_VERBOSE"); + if (var) verbose = 1; + } + + RAOUT(printf("Start\n")); + + while ((t = tests[i++])) { + error = t(); + if (error) { + RAOUT(printf("Failed with error %d!\n", error)); + return error; + } + } + + RAOUT(printf("Success!\n")); + return 0; +} |